<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://nurkiewicz.com/feeds/posts/default" rel="self" type="application/atom+xml" /><link href="https://nurkiewicz.com/" rel="alternate" type="text/html" hreflang="en" /><updated>2026-09-29T13:34:16+02:00</updated><id>https://nurkiewicz.com/feeds/posts/default</id><title type="html">Around IT in 256 seconds</title><subtitle>Podcast for developers, testers, SREs… and their managers. I explain complex and convoluted technologies in a clear way, avoiding buzzwords and hype. Never longer than 4 minutes and 16 seconds.</subtitle><entry><title type="html">Interoperability with Java: Write yourself a compiler, Part VI</title><link href="https://nurkiewicz.com/2026/09/interoperability-with-java-write-yourself-a-compiler.html" rel="alternate" type="text/html" title="Interoperability with Java: Write yourself a compiler, Part VI" /><published>2026-09-28T00:00:00+02:00</published><updated>2026-09-28T00:00:00+02:00</updated><id>https://nurkiewicz.com/2026/09/interoperability-with-java-write-yourself-a-compiler</id><content type="html" xml:base="https://nurkiewicz.com/2026/09/interoperability-with-java-write-yourself-a-compiler.html"><![CDATA[<p>A small addendum to our previous article <a href="/2026/09/generating-java-bytecode-write-yourself-a-compiler.html">where we wrote functional compiler targetting JVM bytecode</a>, let’s discuss interoperability with the JVM.
The biggest win from using a well-established virtual machine is gaining access to the entire ecosystem.</p>

<h2 id="building-the-pl0-class">Building the <code class="language-plaintext highlighter-rouge">PL0</code> class</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre><span class="nv">$ </span><span class="nb">mkdir</span> <span class="nt">-p</span> com/nurkiewicz
<span class="nv">$ </span><span class="nb">echo</span> <span class="s1">'65536 + 65537'</span> | ./jvm-compiler <span class="o">&gt;</span> com/nurkiewicz/PL0.class
</pre></td></tr></tbody></table></code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">echo</code> command writes our one-line PL/0 program to the compiler’s standard input.
The compiler translates it into JVM bytecode and writes to <code class="language-plaintext highlighter-rouge">com/nurkiewicz/PL0.class</code>, matching the generated class name: <code class="language-plaintext highlighter-rouge">com.nurkiewicz.PL0</code>.
From now on, that class is available on the CLASSPATH just like one produced by <code class="language-plaintext highlighter-rouge">javac</code>.</p>

<p>Let’s say I’m a Java developer.
I can write a program in my favorite language like so:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
</pre></td><td class="rouge-code"><pre><span class="kn">package</span> <span class="nn">com.example</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">com.nurkiewicz.PL0</span><span class="o">;</span>

<span class="kd">public</span> <span class="kd">final</span> <span class="kd">class</span> <span class="nc">PL0Interop</span> <span class="o">{</span>
	<span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="nc">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
		<span class="no">PL0</span><span class="o">.</span><span class="na">main</span><span class="o">(</span><span class="kc">null</span><span class="o">);</span>
	<span class="o">}</span>
<span class="o">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Look closely!
This program casually imports the <code class="language-plaintext highlighter-rouge">com.nurkiewicz.PL0</code> class.
The class that we generated from our toy language!
The client code simply calls a method from our compiled class, and it just works!
See for yourself:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre><span class="nv">$ </span>javac <span class="nt">-cp</span> <span class="nb">.</span> com/example/PL0Interop.java
<span class="nv">$ </span>java <span class="nt">-cp</span> <span class="nb">.</span> com.example.PL0Interop

131073
</pre></td></tr></tbody></table></code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">javac</code> command compiles the <code class="language-plaintext highlighter-rouge">PL0Interop.java</code> file into <code class="language-plaintext highlighter-rouge">PL0Interop.class</code>.
The compilation works even though no Java source exists for the referenced <code class="language-plaintext highlighter-rouge">PL0</code> class.
That class is already available as a <code class="language-plaintext highlighter-rouge">.class</code> file, and the <code class="language-plaintext highlighter-rouge">javac</code> compiler doesn’t care that it came from some alien language.
The second line just runs the <code class="language-plaintext highlighter-rouge">PL0Interop</code> program.
It works just fine, printing the result.</p>

<p>Notice that such integration is much broader.
Any other JVM language, like Scala or Kotlin, could interoperate with our language.
Moreover, our toy language can take advantage of the Java standard library - for example, by using <code class="language-plaintext highlighter-rouge">System.out.println()</code> to print results.</p>

<p>But the benefits don’t end here.
A Java compiler compiling the equivalent Java expression would not even emit both <code class="language-plaintext highlighter-rouge">ldc</code> instructions followed by <code class="language-plaintext highlighter-rouge">iadd</code>.
Instead, it would evaluate the constant expression at compile time and emit the hard-coded value <code class="language-plaintext highlighter-rouge">131073</code>.
After all, the result is known at compile time, so why bother the CPU with the addition?
Even though our compiler is not nearly as smart, a JVM implementation with a just-in-time compiler may still be.
If this method becomes hot, the JIT compiler can fold the constant addition and replace it with the result.</p>

<h2 id="adding-line-numbers-mapping">Adding line numbers mapping</h2>

<p>Last but not least, we could add bytecode index to line number mapping, as well as local variable name table.
These pieces of metadata would allow debuggers to step through our code interactively, mapping instructions to our toy language!
Just for fun, I added <code class="language-plaintext highlighter-rouge">LineNumberTable</code> to our generated <code class="language-plaintext highlighter-rouge">.class</code> file.
Makes very little sense, because our language currently supports just one-line programs, but still…</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
</pre></td><td class="rouge-code"><pre><span class="nb">echo</span> <span class="s1">'2 + 40'</span> | ./jvm-compiler <span class="o">&gt;</span> PL0.class<span class="p">;</span> javap <span class="nt">-v</span> <span class="nt">-c</span> PL0.class
...
<span class="o">{</span>
  public static void main<span class="o">(</span>java.lang.String[]<span class="o">)</span><span class="p">;</span>
    descriptor: <span class="o">([</span>Ljava/lang/String<span class="p">;</span><span class="o">)</span>V
    flags: <span class="o">(</span>0x0009<span class="o">)</span> ACC_PUBLIC, ACC_STATIC
    Code:
      <span class="nv">stack</span><span class="o">=</span>3, <span class="nv">locals</span><span class="o">=</span>1, <span class="nv">args_size</span><span class="o">=</span>1
         0: getstatic     <span class="c">#14                 // Field java/lang/System.out:Ljava/io/PrintStream;</span>
         3: iconst_2
         4: bipush        40
         6: iadd
         7: invokevirtual <span class="c">#20                 // Method java/io/PrintStream.println:(I)V</span>
        10: <span class="k">return
      </span>LineNumberTable:
        line 1: 3
<span class="o">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">line 1: 3</code> basically means: bytecode at index 3 (<code class="language-plaintext highlighter-rouge">iconst_2</code>) maps to line 1 of the source file.
This table will allow debuggers to highlight proper line when executing bytecode.</p>

<p>As usual, the source code for this part is available on GitHub under <a href="https://github.com/nurkiewicz/writing-compiler/tree/part-vi"><code class="language-plaintext highlighter-rouge">part-vi</code></a> branch.</p>

<h2 id="write-yourself-a-compiler-series">Write yourself a compiler series</h2>

<ol>
  
    
    <li>
      
        <a href="/2026/07/simplest-interpreter-write-yourself-a-compiler-part-i.html">The simplest interpreter</a>
      
    </li>
  
    
    <li>
      
        <a href="/2026/07/arithmetic-interpreter-write-yourself-a-compiler.html">Arithmetic interpreter</a>
      
    </li>
  
    
    <li>
      
        <a href="/2026/08/compiling-to-intermediate-representation-write-yourself-a-compiler.html">Compiling to intermediate representation</a>
      
    </li>
  
    
    <li>
      
        <a href="/2026/08/your-first-virtual-machine-write-yourself-a-compiler.html">Your First Virtual Machine</a>
      
    </li>
  
    
    <li>
      
        <a href="/2026/09/generating-java-bytecode-write-yourself-a-compiler.html">Generating Java bytecode</a>
      
    </li>
  
    
    <li>
      
        <strong><a href="/2026/09/interoperability-with-java-write-yourself-a-compiler.html">Interoperability with Java</a></strong>
      
    </li>
  
</ol>]]></content><author><name></name></author><category term="writing-compiler" /><category term="compiler" /><category term="interpreter" /><category term="go" /><category term="jvm" /><category term="java" /><summary type="html"><![CDATA[A small addendum to our previous article where we wrote functional compiler targetting JVM bytecode, let’s discuss interoperability with the JVM. The biggest win from using a well-established virtual machine is gaining access to the entire ecosystem.]]></summary></entry><entry><title type="html">Interview with Big Data engineer in 2026</title><link href="https://nurkiewicz.com/2026/09/interview-with-big-data-engineer.html" rel="alternate" type="text/html" title="Interview with Big Data engineer in 2026" /><published>2026-09-25T00:00:00+02:00</published><updated>2026-09-25T00:00:00+02:00</updated><id>https://nurkiewicz.com/2026/09/interview-with-big-data-engineer</id><content type="html" xml:base="https://nurkiewicz.com/2026/09/interview-with-big-data-engineer.html"><![CDATA[<p>I love this parody <a href="https://www.youtube.com/embed/FG8sUgjBGXs">“Interview with Big Data engineer in 2026”</a> so much that I started noting down every hilarious quote I heard.
And, apparently, it’s almost a complete transcript.
Hope you’ll enjoy it as well :-).</p>

<iframe width="560" height="315" src="https://www.youtube.com/embed/FG8sUgjBGXs" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen=""></iframe>

<blockquote>
  <p>We built a real-time pipeline, so leadership can ignore insights… at millisecond speeds. <a href="https://youtu.be/FG8sUgjBGXs?t=0">00:00</a></p>
</blockquote>

<blockquote>
  <p>Big data. Most companies don’t actually have big data, they have medium data. Big data is anything that crashes Excel. <a href="https://youtu.be/FG8sUgjBGXs?t=25">00:25</a></p>
</blockquote>

<blockquote>
  <p>We actually have two big datas… for fault tolerance. <a href="https://youtu.be/FG8sUgjBGXs?t=33">00:33</a></p>
</blockquote>

<blockquote>
  <p>Two wrongs don’t make a right… except in a distributed database. <a href="https://youtu.be/FG8sUgjBGXs?t=36">00:36</a></p>
</blockquote>

<blockquote>
  <p>Big data is different math. 1+1 ≈ 1.9. 95% confidence interval. <a href="https://youtu.be/FG8sUgjBGXs?t=40">00:40</a></p>
</blockquote>

<blockquote>
  <p>Our 15-million-row dataset has 800 columns. The rows aren’t wide. They are panoramic. <a href="https://youtu.be/FG8sUgjBGXs?t=47">00:47</a></p>
</blockquote>

<blockquote>
  <p>If the join fits in memory, it belongs in Postgres. <a href="https://youtu.be/FG8sUgjBGXs?t=54">00:54</a></p>
</blockquote>

<blockquote>
  <p>Our architecture preserves every invariant except the budget. <a href="https://youtu.be/FG8sUgjBGXs?t=57">00:57</a></p>
</blockquote>

<blockquote>
  <p>Our cloud bill was $340,000. We don’t know if the processing even ran. That was just observability. <a href="https://youtu.be/FG8sUgjBGXs?t=61">01:01</a></p>
</blockquote>

<blockquote>
  <p>We support real-time processing, of course, unless the event arrives early, late, duplicated, malformed, reordered. <a href="https://youtu.be/FG8sUgjBGXs?t=68">01:08</a></p>
</blockquote>

<blockquote>
  <p>Eventual consistency was too hard, so we went with ‘immediately inaccurate’. <a href="https://youtu.be/FG8sUgjBGXs?t=78">01:18</a></p>
</blockquote>

<blockquote>
  <p>70% of records are accurate. That’s higher than the weather forecast. <a href="https://youtu.be/FG8sUgjBGXs?t=82">01:22</a></p>
</blockquote>

<blockquote>
  <p>The numbers are not wrong. They are early. Wait two, three, maybe… four days, they will be late. <a href="https://youtu.be/FG8sUgjBGXs?t=86">01:26</a></p>
</blockquote>

<blockquote>
  <p>‘Final numbers’ mean numbers we have stopped refreshing… <a href="https://youtu.be/FG8sUgjBGXs?t=95">01:35</a></p>
</blockquote>

<blockquote>
  <p>The slide says we handle billions of events. It does not say we handle them well. <a href="https://youtu.be/FG8sUgjBGXs?t=100">01:40</a></p>
</blockquote>

<blockquote>
  <p>It is only truly idempotent if all availability zones go down equally during the outage. <a href="https://youtu.be/FG8sUgjBGXs?t=105">01:45</a></p>
</blockquote>

<blockquote>
  <p>Management asked us to build a data lake, but it wasn’t scalable enough. So McKinsey asked us to build a data swamp. <a href="https://youtu.be/FG8sUgjBGXs?t=111">01:51</a></p>
</blockquote>

<blockquote>
  <p>The project was declared successful before all the metrics arrived. We call that watermarking. <a href="https://youtu.be/FG8sUgjBGXs?t=121">02:01</a></p>
</blockquote>

<blockquote>
  <p>The dashboard is green because the job that checks if the dashboard is wrong is red. <a href="https://youtu.be/FG8sUgjBGXs?t=127">02:07</a></p>
</blockquote>

<blockquote>
  <p>A production workaround becomes durable the moment the engineer who wrote it leaves. <a href="https://youtu.be/FG8sUgjBGXs?t=137">02:17</a></p>
</blockquote>

<blockquote>
  <p>Yes, we removed an unused column, and it broke Portugal. <a href="https://youtu.be/FG8sUgjBGXs?t=142">02:22</a></p>
</blockquote>

<blockquote>
  <p>The bug has existed for eight years. At this point, it’s an interface. <a href="https://youtu.be/FG8sUgjBGXs?t=146">02:26</a></p>
</blockquote>

<blockquote>
  <p>This is our architecture. Very simple. Kafka, Airflow, Databricks, Flink, Spark, SQL, Iceberg, S3, Presto, dbt. <a href="https://youtu.be/FG8sUgjBGXs?t=155">02:35</a></p>
</blockquote>

<blockquote>
  <p>What does dbt stand for? <em>Dialectical behavioral therapy</em>. <a href="https://youtu.be/FG8sUgjBGXs?t=166">02:46</a></p>
</blockquote>

<blockquote>
  <p>We have the most modern stack in the industry. Only 10% is COBOL. <a href="https://youtu.be/FG8sUgjBGXs?t=171">02:51</a></p>
</blockquote>

<blockquote>
  <p>HDFS is not dead. It’s waiting for someone with root access. <a href="https://youtu.be/FG8sUgjBGXs?t=176">02:56</a></p>
</blockquote>

<blockquote>
  <p>We migrated from cron to Airflow for reliability. Then we wrote a cron job to check if Airflow silently died. <a href="https://youtu.be/FG8sUgjBGXs?t=179">02:59</a></p>
</blockquote>

<blockquote>
  <p>We would switch from Databricks to Excel. If it wasn’t for date parsing. <a href="https://youtu.be/FG8sUgjBGXs?t=186">03:06</a></p>
</blockquote>

<blockquote>
  <p>We are a data-driven company, of course. Every week somebody reads the invoices out of PDFs and types the data into Excel. Running on Kubernetes. <a href="https://youtu.be/FG8sUgjBGXs?t=191">03:11</a></p>
</blockquote>

<blockquote>
  <p>We moved from Hive tables to Iceberg tables because we wanted the metadata to fail… to fail with better governance. <a href="https://youtu.be/FG8sUgjBGXs?t=200">03:20</a></p>
</blockquote>

<blockquote>
  <p>Whenever we lose a backup, we reconstruct what the customer did from the logs. No, that’s one of our industry’s best practices. <a href="https://youtu.be/FG8sUgjBGXs?t=210">03:30</a></p>
</blockquote>

<blockquote>
  <p>Our customers didn’t even know. To be honest, most of our customers don’t even use our product anyway. <a href="https://youtu.be/FG8sUgjBGXs?t=218">03:38</a></p>
</blockquote>

<blockquote>
  <p>The board deck says petabyte scale, yeah. Scale is a direction, not a quantity. I checked with legal. <a href="https://youtu.be/FG8sUgjBGXs?t=223">03:43</a></p>
</blockquote>

<blockquote>
  <p>Management says 1PB. I checked, yeah, we only process 18TB, but we round up. <a href="https://youtu.be/FG8sUgjBGXs?t=229">03:49</a></p>
</blockquote>

<blockquote>
  <p>Our P99 is 4 minutes, but the P50 is excellent, and most people are median. <a href="https://youtu.be/FG8sUgjBGXs?t=235">03:55</a></p>
</blockquote>

<blockquote>
  <p>Streaming gets simple once you understand watermarks, event time, processing time, state, backpressure. <a href="https://youtu.be/FG8sUgjBGXs?t=242">04:02</a></p>
</blockquote>

<blockquote>
  <p>Batch and streaming produce different numbers. We average them. <a href="https://youtu.be/FG8sUgjBGXs?t=250">04:10</a></p>
</blockquote>

<blockquote>
  <p>Our recommendation pipeline had three hours of Kafka lag. So technically it was a real-time recommendation for your past self. <a href="https://youtu.be/FG8sUgjBGXs?t=256">04:16</a></p>
</blockquote>

<blockquote>
  <p>We decoupled storage and compute. Now the storage team and the compute team have separate outages. <a href="https://youtu.be/FG8sUgjBGXs?t=267">04:27</a></p>
</blockquote>

<blockquote>
  <p>The data team? The data team has excellent isolation. We haven’t spoken to Product in seven months. <a href="https://youtu.be/FG8sUgjBGXs?t=277">04:37</a></p>
</blockquote>

<blockquote>
  <p>All problems become your problems at 1TB/hour. <a href="https://youtu.be/FG8sUgjBGXs?t=283">04:43</a></p>
</blockquote>

<blockquote>
  <p>Exactly-once processing is easy. You just need… two topics, transactions, offsets, recovery states, and a relaxed interpretation of “easy.” <a href="https://youtu.be/FG8sUgjBGXs?t=287">04:47</a></p>
</blockquote>

<blockquote>
  <p>GDPR in a data warehouse. There’s a funny joke. <a href="https://youtu.be/FG8sUgjBGXs?t=297">04:57</a></p>
</blockquote>

<blockquote>
  <p>We anonymized the data… by removing the column ‘name’. <a href="https://youtu.be/FG8sUgjBGXs?t=300">05:00</a></p>
</blockquote>

<blockquote>
  <p>We persist it in places nobody knows how to query. <a href="https://youtu.be/FG8sUgjBGXs?t=304">05:04</a></p>
</blockquote>

<blockquote>
  <p>The only thing in this company with strong durability is a bad architectural decision. <a href="https://youtu.be/FG8sUgjBGXs?t=307">05:07</a></p>
</blockquote>

<blockquote>
  <p>Salary, job security, holidays, pick two. CAP-compliant. <a href="https://youtu.be/FG8sUgjBGXs?t=313">05:13</a></p>
</blockquote>

<blockquote>
  <p>Can we have this by Friday? In distributed systems, what does Friday mean? Before the meeting, it’s only a partial order. <a href="https://youtu.be/FG8sUgjBGXs?t=318">05:18</a></p>
</blockquote>

<blockquote>
  <p>The project isn’t late. The clocks just disagree. It’s a distributed organization. <a href="https://youtu.be/FG8sUgjBGXs?t=326">05:26</a></p>
</blockquote>

<blockquote>
  <p>Every quarter, we solve capacity problems… by increasing input. <a href="https://youtu.be/FG8sUgjBGXs?t=331">05:31</a></p>
</blockquote>

<blockquote>
  <p>We maintain system availability by shedding… by shedding Jira tickets. <a href="https://youtu.be/FG8sUgjBGXs?t=335">05:35</a></p>
</blockquote>

<blockquote>
  <p>Last year, they fired 80% of our team as part of our… “operational transformation.” They should have used CRDTs instead. <a href="https://youtu.be/FG8sUgjBGXs?t=340">05:40</a></p>
</blockquote>

<blockquote>
  <p>A backfill is when you fix one historical bug by introducing one historical bug by introducing one historical bug by introducing a brand-new historical bug. <a href="https://youtu.be/FG8sUgjBGXs?t=348">05:48</a></p>
</blockquote>

<blockquote>
  <p>The Snowflake warehouse runs a 2-second query every minute. Very sophisticated way of never letting it turn off. <a href="https://youtu.be/FG8sUgjBGXs?t=354">05:54</a></p>
</blockquote>

<blockquote>
  <p>Artificial intelligence? No. We still rely on human… human single point of failure. <a href="https://youtu.be/FG8sUgjBGXs?t=361">06:01</a></p>
</blockquote>

<blockquote>
  <p>We migrated to Apache Iceberg this year… Quick 43-month migration. <a href="https://youtu.be/FG8sUgjBGXs?t=393">06:33</a></p>
</blockquote>

<blockquote>
  <p>We still use protobuf. Yes, we are still confused, but at least confused… strongly typed. <a href="https://youtu.be/FG8sUgjBGXs?t=399">06:39</a></p>
</blockquote>

<blockquote>
  <p>Some say data is the new oil. We cannot stop producing it, and we will pay to clean it up for years. <a href="https://youtu.be/FG8sUgjBGXs?t=405">06:45</a></p>
</blockquote>

<blockquote>
  <p>From what I see, data is the new asbestos. <a href="https://youtu.be/FG8sUgjBGXs?t=413">06:53</a></p>
</blockquote>

<blockquote>
  <p>The AI teams… spent $4 million and it called investment. I spent $340,000… and this called a review. <a href="https://youtu.be/FG8sUgjBGXs?t=415">06:55</a></p>
</blockquote>

<blockquote>
  <p>I’m in a FinOps review every Thursday. The AI team has never, ever been to FinOps. They have a different word for money. <a href="https://youtu.be/FG8sUgjBGXs?t=423">07:03</a></p>
</blockquote>

<blockquote>
  <p>My cluster is a cost center, their cluster is a strategy. Same rack in AWS. <a href="https://youtu.be/FG8sUgjBGXs?t=431">07:11</a></p>
</blockquote>

<blockquote>
  <p>A soft outage is when the checkpoint directory grows faster than S3 versioning can bill. <a href="https://youtu.be/FG8sUgjBGXs?t=444">07:24</a></p>
</blockquote>

<blockquote>
  <p>Back in the days, big data means you bring the intelligence to the data. That’s why I go straight to Big Data center. We have two big data centers… for fault tolerance. <a href="https://youtu.be/FG8sUgjBGXs?t=451">07:31</a></p>
</blockquote>

<blockquote>
  <p>But the team came with Airflow graph. We use what? 1, 2, 3, 4, 13, 14, 23, 6400 services… just to clean the logs. <a href="https://youtu.be/FG8sUgjBGXs?t=474">07:54</a></p>
</blockquote>]]></content><author><name></name></author><summary type="html"><![CDATA[I love this parody “Interview with Big Data engineer in 2026” so much that I started noting down every hilarious quote I heard. And, apparently, it’s almost a complete transcript. Hope you’ll enjoy it as well :-).]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://nurkiewicz.com/assets/img/interview-with-big-data-engineer/hero.png" /><media:content medium="image" url="https://nurkiewicz.com/assets/img/interview-with-big-data-engineer/hero.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Generating Java bytecode: Write yourself a compiler, Part V</title><link href="https://nurkiewicz.com/2026/09/generating-java-bytecode-write-yourself-a-compiler.html" rel="alternate" type="text/html" title="Generating Java bytecode: Write yourself a compiler, Part V" /><published>2026-09-15T00:00:00+02:00</published><updated>2026-09-15T00:00:00+02:00</updated><id>https://nurkiewicz.com/2026/09/generating-java-bytecode-write-yourself-a-compiler</id><content type="html" xml:base="https://nurkiewicz.com/2026/09/generating-java-bytecode-write-yourself-a-compiler.html"><![CDATA[<p>Last time <a href="/2026/08/compiling-to-intermediate-representation-write-yourself-a-compiler.html">we created a virtual machine for our toy language</a>.
I think you can agree that designing a language which barely recognizes expressions like <code class="language-plaintext highlighter-rouge">2 + 3</code> and building a brand-new virtual machine for it seems a bit tedious.
So what about keeping our microscopic language for now, but running it on a real, production-ready, battle-proven virtual machine?
Like the <a href="https://en.wikipedia.org/wiki/Java_virtual_machine">Java Virtual Machine</a>?
Our task for today is to emit JVM bytecode in the form of a valid <code class="language-plaintext highlighter-rouge">.class</code> file.
That file can then be fed directly to the JVM to run our program.</p>

<p>How’s that even possible?
Isn’t the Java Virtual Machine made for, you know, Java?
Not really. There are dozens of programming languages that target the JVM, including <a href="https://www.scala-lang.org">Scala</a>, <a href="https://kotlinlang.org">Kotlin</a>, <a href="https://clojure.org">Clojure</a>, and <a href="https://groovy-lang.org">Groovy</a>, just to <a href="https://en.wikipedia.org/wiki/List_of_JVM_languages">name a few</a>.
So, let’s just say we are building yet another JVM language!</p>

<h2 id="getting-familiar-with-the-jvm-bytecode">Getting familiar with the JVM bytecode</h2>

<p>Superficially, JVM bytecode is very similar to the IR we built.
Here’s the relevant code of our compiler, generating JVM-compatible bytecode:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
</pre></td><td class="rouge-code"><pre><span class="n">code</span> <span class="o">=</span> <span class="n">appendPush</span><span class="p">(</span><span class="n">code</span><span class="p">,</span> <span class="n">expr</span><span class="o">.</span><span class="n">left</span><span class="p">,</span> <span class="n">pool</span><span class="p">)</span>
<span class="n">code</span> <span class="o">=</span> <span class="n">appendPush</span><span class="p">(</span><span class="n">code</span><span class="p">,</span> <span class="n">expr</span><span class="o">.</span><span class="n">right</span><span class="p">,</span> <span class="n">pool</span><span class="p">)</span>
<span class="n">opcode</span><span class="p">,</span> <span class="n">ok</span> <span class="o">:=</span> <span class="k">map</span><span class="p">[</span><span class="kt">byte</span><span class="p">]</span><span class="kt">byte</span><span class="p">{</span>
  <span class="sc">'+'</span><span class="o">:</span> <span class="n">opcodeIadd</span><span class="p">,</span>
  <span class="sc">'-'</span><span class="o">:</span> <span class="n">opcodeIsub</span><span class="p">,</span>
  <span class="sc">'*'</span><span class="o">:</span> <span class="n">opcodeImul</span><span class="p">,</span>
  <span class="sc">'/'</span><span class="o">:</span> <span class="n">opcodeIdiv</span><span class="p">,</span>
<span class="p">}[</span><span class="n">expr</span><span class="o">.</span><span class="n">op</span><span class="p">]</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Opcodes are defined e.g. <a href="https://en.wikipedia.org/wiki/List_of_JVM_bytecode_instructions">here</a>.
So, push the first operand, push the second operand, and execute the operator instruction.
The instruction pops the two operands and pushes the result back.
Just like our custom VM.
For example, the expression <code class="language-plaintext highlighter-rouge">2 + 3</code> results in the following bytecode:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre>iconst_2
iconst_3
iadd
</pre></td></tr></tbody></table></code></pre></div></div>

<p>However, the instruction used to push an integer onto the operand stack depends on its value:</p>

<table>
  <thead>
    <tr>
      <th>Value (N)</th>
      <th>Opcode</th>
      <th>Argument</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>-1</td>
      <td><code class="language-plaintext highlighter-rouge">iconst_m1</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>0 to 5</td>
      <td><code class="language-plaintext highlighter-rouge">iconst_N</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>-128 to 127</td>
      <td><code class="language-plaintext highlighter-rouge">bipush</code></td>
      <td>N</td>
    </tr>
    <tr>
      <td>-32768 to 32767</td>
      <td><code class="language-plaintext highlighter-rouge">sipush</code></td>
      <td>N</td>
    </tr>
    <tr>
      <td>N</td>
      <td><code class="language-plaintext highlighter-rouge">ldc</code> or <code class="language-plaintext highlighter-rouge">ldc_w</code></td>
      <td>C</td>
    </tr>
  </tbody>
</table>

<p>The JVM uses several instruction families just to push an integer onto the operand stack (!)
For values between <code class="language-plaintext highlighter-rouge">-1</code> and <code class="language-plaintext highlighter-rouge">5</code>, there’s a dedicated instruction for each number (without arguments).
Other values fitting in a signed byte or signed 16-bit integer are stored directly in the <code class="language-plaintext highlighter-rouge">bipush</code> or <code class="language-plaintext highlighter-rouge">sipush</code> instruction.
Once a constant no longer fits in 16 bits, we use <code class="language-plaintext highlighter-rouge">ldc</code> or <code class="language-plaintext highlighter-rouge">ldc_w</code>.
These instructions load a value from the so-called <em>constant pool</em> at index <code class="language-plaintext highlighter-rouge">C</code>; <code class="language-plaintext highlighter-rouge">ldc_w</code> supports a wider constant-pool index than <code class="language-plaintext highlighter-rouge">ldc</code>.</p>

<h2 id="constant-pool">Constant pool</h2>

<p>The designers of the Java Virtual Machine decided that constants and symbolic references used by instructions should be kept outside the method code.
That special place is called the <em>constant pool</em>.
Moreover, if the same constant is used in more than one place, we can store it once and reuse its constant-pool entry.
The constant pool also contains strings, class and method references, and a bunch of other fixed values.
This makes our lives a little bit more complicated.
While <code class="language-plaintext highlighter-rouge">2 + 3</code> uses one set of bytecode instructions:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre>iconst_2
iconst_3
iadd
</pre></td></tr></tbody></table></code></pre></div></div>

<p>The same expression, but with higher values (<code class="language-plaintext highlighter-rouge">65536 + 65537</code>), is represented by completely different instructions:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre>3: ldc           #22                 // int 65536
5: ldc           #23                 // int 65537
7: iadd
</pre></td></tr></tbody></table></code></pre></div></div>

<p>The instructions <code class="language-plaintext highlighter-rouge">ldc #22</code> and <code class="language-plaintext highlighter-rouge">ldc #23</code> basically mean “<em>load the values located at indices 22 and 23, respectively, in the constant pool.</em>”
Because the constant pool appears before the methods in a <code class="language-plaintext highlighter-rouge">.class</code> file, we must collect its entries before serializing the complete class.</p>

<h2 id="building-a-fully-functional-class-file">Building a fully functional <code class="language-plaintext highlighter-rouge">.class</code> file</h2>

<p>There used to be a ton of ceremony to write a simple <code class="language-plaintext highlighter-rouge">"Hello, world!"</code> program in Java.
There’s also a ton of ceremony in creating a proper Java class.
It’s almost as if JVM bytecode is supposed to be as verbose as the language itself.
Boilerplate tradition.</p>

<p>There aren’t many good Go libraries for creating Java <code class="language-plaintext highlighter-rouge">.class</code> files, so I’m building everything from scratch.
Just look how much structure we have to output for a minimal class:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
</pre></td><td class="rouge-code"><pre><span class="k">var</span> <span class="n">class</span> <span class="n">bytes</span><span class="o">.</span><span class="n">Buffer</span>
<span class="n">u4</span><span class="p">(</span><span class="o">&amp;</span><span class="n">class</span><span class="p">,</span> <span class="m">0xCAFEBABE</span><span class="p">)</span>                  <span class="c">// magic</span>
<span class="n">u2</span><span class="p">(</span><span class="o">&amp;</span><span class="n">class</span><span class="p">,</span> <span class="m">0</span><span class="p">)</span>                           <span class="c">// minor_version</span>
<span class="n">u2</span><span class="p">(</span><span class="o">&amp;</span><span class="n">class</span><span class="p">,</span> <span class="m">52</span><span class="p">)</span>                          <span class="c">// major_version: Java 8</span>
<span class="n">u2</span><span class="p">(</span><span class="o">&amp;</span><span class="n">class</span><span class="p">,</span> <span class="kt">uint16</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">pool</span><span class="o">.</span><span class="n">entries</span><span class="p">)</span><span class="o">+</span><span class="m">1</span><span class="p">))</span> <span class="c">// constant_pool_count</span>
<span class="k">for</span> <span class="n">_</span><span class="p">,</span> <span class="n">entry</span> <span class="o">:=</span> <span class="k">range</span> <span class="n">pool</span><span class="o">.</span><span class="n">entries</span> <span class="p">{</span>
  <span class="n">class</span><span class="o">.</span><span class="n">Write</span><span class="p">(</span><span class="n">entry</span><span class="p">)</span>
<span class="p">}</span>
<span class="n">u2</span><span class="p">(</span><span class="o">&amp;</span><span class="n">class</span><span class="p">,</span> <span class="m">0x0021</span><span class="p">)</span>                                             <span class="c">// public, super</span>
<span class="n">u2</span><span class="p">(</span><span class="o">&amp;</span><span class="n">class</span><span class="p">,</span> <span class="n">thisClass</span><span class="p">)</span>                                          <span class="c">// this_class</span>
<span class="n">u2</span><span class="p">(</span><span class="o">&amp;</span><span class="n">class</span><span class="p">,</span> <span class="n">superClass</span><span class="p">)</span>                                         <span class="c">// super_class</span>
<span class="n">u2</span><span class="p">(</span><span class="o">&amp;</span><span class="n">class</span><span class="p">,</span> <span class="m">0</span><span class="p">)</span>                                                  <span class="c">// interfaces_count</span>
<span class="n">u2</span><span class="p">(</span><span class="o">&amp;</span><span class="n">class</span><span class="p">,</span> <span class="m">0</span><span class="p">)</span>                                                  <span class="c">// fields_count</span>
<span class="n">u2</span><span class="p">(</span><span class="o">&amp;</span><span class="n">class</span><span class="p">,</span> <span class="m">1</span><span class="p">)</span>                                                  <span class="c">// methods_count</span>
<span class="n">method</span><span class="p">(</span><span class="o">&amp;</span><span class="n">class</span><span class="p">,</span> <span class="n">mainName</span><span class="p">,</span> <span class="n">mainDescriptor</span><span class="p">,</span> <span class="n">codeName</span><span class="p">,</span> <span class="m">3</span><span class="p">,</span> <span class="m">1</span><span class="p">,</span> <span class="n">code</span><span class="p">)</span> <span class="c">// max_stack, max_locals</span>
<span class="n">u2</span><span class="p">(</span><span class="o">&amp;</span><span class="n">class</span><span class="p">,</span> <span class="m">1</span><span class="p">)</span>                                                  <span class="c">// attributes_count</span>
<span class="n">u2</span><span class="p">(</span><span class="o">&amp;</span><span class="n">class</span><span class="p">,</span> <span class="n">sourceFileName</span><span class="p">)</span>                                     <span class="c">// attribute_name_index</span>
<span class="n">u4</span><span class="p">(</span><span class="o">&amp;</span><span class="n">class</span><span class="p">,</span> <span class="m">2</span><span class="p">)</span>                                                  <span class="c">// attribute_length</span>
<span class="n">u2</span><span class="p">(</span><span class="o">&amp;</span><span class="n">class</span><span class="p">,</span> <span class="n">sourceFile</span><span class="p">)</span>                                         <span class="c">// sourcefile_index</span>
<span class="k">return</span> <span class="n">class</span><span class="o">.</span><span class="n">Bytes</span><span class="p">(),</span> <span class="no">nil</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>You can recognize the infamous <code class="language-plaintext highlighter-rouge">CAFE BABE</code> header, followed by the Java version we target.
I’m old-school, so our compiler will target Java 8.
Thanks to Java’s legendary backward compatibility, such a class can also run on newer JVMs.
Just for fun, here’s a complete Java class running the <code class="language-plaintext highlighter-rouge">65536 + 65537</code> “program”:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
</pre></td><td class="rouge-code"><pre>$ echo '65536 + 65537' | ./jvm-compiler &gt; PL0.class
$ xxd -u -g1 -c 16 PL0.class
00000000: CA FE BA BE 00 00 00 34 00 18 01 00 12 63 6F 6D  .......4.....com
00000010: 2F 6E 75 72 6B 69 65 77 69 63 7A 2F 50 4C 30 07  /nurkiewicz/PL0.
00000020: 00 01 01 00 10 6A 61 76 61 2F 6C 61 6E 67 2F 4F  .....java/lang/O
00000030: 62 6A 65 63 74 07 00 03 01 00 04 43 6F 64 65 01  bject......Code.
00000040: 00 04 6D 61 69 6E 01 00 16 28 5B 4C 6A 61 76 61  ..main...([Ljava
00000050: 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B 29 56 01  /lang/String;)V.
00000060: 00 10 6A 61 76 61 2F 6C 61 6E 67 2F 53 79 73 74  ..java/lang/Syst
00000070: 65 6D 07 00 08 01 00 03 6F 75 74 01 00 15 4C 6A  em......out...Lj
00000080: 61 76 61 2F 69 6F 2F 50 72 69 6E 74 53 74 72 65  ava/io/PrintStre
00000090: 61 6D 3B 0C 00 0A 00 0B 09 00 09 00 0C 01 00 13  am;.............
000000a0: 6A 61 76 61 2F 69 6F 2F 50 72 69 6E 74 53 74 72  java/io/PrintStr
000000b0: 65 61 6D 07 00 0E 01 00 07 70 72 69 6E 74 6C 6E  eam......println
000000c0: 01 00 04 28 49 29 56 0C 00 10 00 11 0A 00 0F 00  ...(I)V.........
000000d0: 12 01 00 0A 53 6F 75 72 63 65 46 69 6C 65 01 00  ....SourceFile..
000000e0: 07 50 4C 30 2E 70 6C 30 03 00 01 00 00 03 00 01  .PL0.pl0........
000000f0: 00 01 00 21 00 02 00 04 00 00 00 00 00 01 00 09  ...!............
00000100: 00 06 00 07 00 01 00 05 00 00 00 18 00 03 00 01  ................
00000110: 00 00 00 0C B2 00 0D 12 16 12 17 60 B6 00 13 B1  ...........`....
00000120: 00 00 00 00 00 01 00 14 00 00 00 02 00 15        ..............

</pre></td></tr></tbody></table></code></pre></div></div>

<p>Most of the above is just <code class="language-plaintext highlighter-rouge">.class</code> file structure and constant-pool data.
The executable code of our <code class="language-plaintext highlighter-rouge">main</code> method takes just a few bytes:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>00 00 00 0C B2 00 0D 12 16 12 17 60 B6 00 13 B1
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Within that method, bytes <code class="language-plaintext highlighter-rouge">12 16 12 17 60</code> represent the arithmetic expression itself.
These bytes correspond to the following bytecode:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre>3: ldc           #22                 // int 65536
5: ldc           #23                 // int 65537
7: iadd
</pre></td></tr></tbody></table></code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">#22</code> and <code class="language-plaintext highlighter-rouge">#23</code> are just indices in the constant pool:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre>Constant pool:
  #22 = Integer            65536
  #23 = Integer            65537
</pre></td></tr></tbody></table></code></pre></div></div>

<p>The constant pool is, of course, also part of the <code class="language-plaintext highlighter-rouge">.class</code> file:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre>000000e0: 07 50 4C 30 2E 70 6C 30 03 00 01 00 00 03 00 01  .PL0.pl0........
000000f0: 00 01 00 21 00 02 00 04 00 00 00 00 00 01 00 09  ...!............
</pre></td></tr></tbody></table></code></pre></div></div>

<p>To be precise, the following bytes represent these two entries.
The <code class="language-plaintext highlighter-rouge">03</code> tag means <code class="language-plaintext highlighter-rouge">Integer</code>, followed by the big-endian representation of <code class="language-plaintext highlighter-rouge">0x00010000</code> or <code class="language-plaintext highlighter-rouge">0x00010001</code>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre>03 00 01 00 00 
03 00 01 00 01 
</pre></td></tr></tbody></table></code></pre></div></div>

<h2 id="running-on-the-jvm">Running on the JVM</h2>

<p>Let’s compile our tiny program and see if Java actually recognizes it!</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="nv">$ </span><span class="nb">echo</span> <span class="s1">'65536 + 65537'</span> | ./jvm-compiler <span class="o">&gt;</span> com/nurkiewicz/PL0.class
<span class="nv">$ </span>java com.nurkiewicz.PL0
131073
</pre></td></tr></tbody></table></code></pre></div></div>

<p><strong>It’s alive!</strong>
The first command feeds the <code class="language-plaintext highlighter-rouge">jvm-compiler</code> process (full source code here: <a href="https://github.com/nurkiewicz/writing-compiler/blob/part-v/cmd/jvm-compiler/main.go"><code class="language-plaintext highlighter-rouge">main.go</code></a>).
The second command executes the generated binary <code class="language-plaintext highlighter-rouge">.class</code> file on a real Java Virtual Machine.
We managed to write a compiler which takes a program written in our imaginary (and extremely simple) language and creates a proper Java class.</p>

<h2 id="disassembling-the-generated-program">Disassembling the generated program</h2>

<p>Java ships with the <code class="language-plaintext highlighter-rouge">javap</code> tool, which takes a <code class="language-plaintext highlighter-rouge">.class</code> file and disassembles it back into a human-readable representation of its bytecode.
Here’s the complete program we managed to generate (cleaned up for brevity):</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
</pre></td><td class="rouge-code"><pre><span class="nv">$ </span>javap <span class="nt">-v</span> <span class="nt">-c</span> PL0.class
public class com.nurkiewicz.PL0
  minor version: 0
  major version: 52
  flags: <span class="o">(</span>0x0021<span class="o">)</span> ACC_PUBLIC, ACC_SUPER
  this_class: <span class="c">#2                          // com/nurkiewicz/PL0</span>
  super_class: <span class="c">#4                         // java/lang/Object</span>
  interfaces: 0, fields: 0, methods: 1, attributes: 1
Constant pool:
   <span class="c">#1 = Utf8               com/nurkiewicz/PL0</span>
   <span class="c">#2 = Class              #1             // com/nurkiewicz/PL0</span>
   <span class="c">#3 = Utf8               java/lang/Object</span>
   <span class="c">#4 = Class              #3             // java/lang/Object</span>
   <span class="c">#5 = Utf8               Code</span>
   <span class="c">#6 = Utf8               main</span>
   <span class="c">#7 = Utf8               ([Ljava/lang/String;)V</span>
   <span class="c">#8 = Utf8               java/lang/System</span>
   <span class="c">#9 = Class              #8             // java/lang/System</span>
  <span class="c">#10 = Utf8               out</span>
  <span class="c">#11 = Utf8               Ljava/io/PrintStream;</span>
  <span class="c">#12 = NameAndType        #10:#11        // out:Ljava/io/PrintStream;</span>
  <span class="c">#13 = Fieldref           #9.#12         // java/lang/System.out:Ljava/io/PrintStream;</span>
  <span class="c">#14 = Utf8               java/io/PrintStream</span>
  <span class="c">#15 = Class              #14            // java/io/PrintStream</span>
  <span class="c">#16 = Utf8               println</span>
  <span class="c">#17 = Utf8               (I)V</span>
  <span class="c">#18 = NameAndType        #16:#17        // println:(I)V</span>
  <span class="c">#19 = Methodref          #15.#18        // java/io/PrintStream.println:(I)V</span>
  <span class="c">#20 = Utf8               SourceFile</span>
  <span class="c">#21 = Utf8               PL0.pl0</span>
  <span class="c">#22 = Integer            65536</span>
  <span class="c">#23 = Integer            65537</span>
<span class="o">{</span>
  public static void main<span class="o">(</span>java.lang.String[]<span class="o">)</span><span class="p">;</span>
    Code:
      <span class="nv">stack</span><span class="o">=</span>3, <span class="nv">locals</span><span class="o">=</span>1, <span class="nv">args_size</span><span class="o">=</span>1
         0: getstatic     <span class="c">#13                 // Field java/lang/System.out:Ljava/io/PrintStream;</span>
         3: ldc           <span class="c">#22                 // int 65536</span>
         5: ldc           <span class="c">#23                 // int 65537</span>
         7: iadd
         8: invokevirtual <span class="c">#19                 // Method java/io/PrintStream.println:(I)V</span>
        11: <span class="k">return</span>
<span class="o">}</span>
SourceFile: <span class="s2">"PL0.pl0"</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>You can see the entire constant pool, including references to our base class (<code class="language-plaintext highlighter-rouge">java/lang/Object</code>) and the <code class="language-plaintext highlighter-rouge">PrintStream.println</code> method used for printing to <code class="language-plaintext highlighter-rouge">stdout</code>.
Our tiny little program is there, at the very bottom.
Just for fun, we can include the source file name, which will appear in many debugging tools.</p>

<p>As usual, the source code for this part is available on GitHub under <a href="https://github.com/nurkiewicz/writing-compiler/tree/part-v"><code class="language-plaintext highlighter-rouge">part-v</code></a> branch.</p>

<h2 id="write-yourself-a-compiler-series">Write yourself a compiler series</h2>

<ol>
  
    
    <li>
      
        <a href="/2026/07/simplest-interpreter-write-yourself-a-compiler-part-i.html">The simplest interpreter</a>
      
    </li>
  
    
    <li>
      
        <a href="/2026/07/arithmetic-interpreter-write-yourself-a-compiler.html">Arithmetic interpreter</a>
      
    </li>
  
    
    <li>
      
        <a href="/2026/08/compiling-to-intermediate-representation-write-yourself-a-compiler.html">Compiling to intermediate representation</a>
      
    </li>
  
    
    <li>
      
        <a href="/2026/08/your-first-virtual-machine-write-yourself-a-compiler.html">Your First Virtual Machine</a>
      
    </li>
  
    
    <li>
      
        <strong><a href="/2026/09/generating-java-bytecode-write-yourself-a-compiler.html">Generating Java bytecode</a></strong>
      
    </li>
  
    
    <li>
      
        <a href="/2026/09/interoperability-with-java-write-yourself-a-compiler.html">Interoperability with Java</a>
      
    </li>
  
</ol>]]></content><author><name></name></author><category term="writing-compiler" /><category term="compiler" /><category term="interpreter" /><category term="go" /><category term="jvm" /><category term="java" /><summary type="html"><![CDATA[Last time we created a virtual machine for our toy language. I think you can agree that designing a language which barely recognizes expressions like 2 + 3 and building a brand-new virtual machine for it seems a bit tedious. So what about keeping our microscopic language for now, but running it on a real, production-ready, battle-proven virtual machine? Like the Java Virtual Machine? Our task for today is to emit JVM bytecode in the form of a valid .class file. That file can then be fed directly to the JVM to run our program.]]></summary></entry><entry><title type="html">Your First Virtual Machine: Write yourself a compiler, Part IV</title><link href="https://nurkiewicz.com/2026/08/your-first-virtual-machine-write-yourself-a-compiler.html" rel="alternate" type="text/html" title="Your First Virtual Machine: Write yourself a compiler, Part IV" /><published>2026-08-25T00:00:00+02:00</published><updated>2026-08-25T00:00:00+02:00</updated><id>https://nurkiewicz.com/2026/08/your-first-virtual-machine-write-yourself-a-compiler</id><content type="html" xml:base="https://nurkiewicz.com/2026/08/your-first-virtual-machine-write-yourself-a-compiler.html"><![CDATA[<p>In the previous article, <a href="/2026/08/compiling-to-intermediate-representation-write-yourself-a-compiler.html">we emitted an intermediate representation (IR) for our programming language</a> that is easier to process than source code.
However, we did not build a program that could read and execute that IR.
Such a program is called a virtual machine.
Technically, it’s still an interpreter.
But instead of interpreting source code, it interprets IR.
Our IR is binary, compact, structured, and generally faster to interpret than the original source.
Moreover, as you’ll see later, the VM’s instruction set can express programs that our source language cannot produce yet!</p>

<h2 id="how-ir-works">How IR works</h2>

<p>As a reminder, an expression like this <code class="language-plaintext highlighter-rouge">2 + 3</code> is translated to the following IR:</p>

<table>
  <thead>
    <tr>
      <th>Bytes</th>
      <th>Explanation</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">01 00 00 00 02</code></td>
      <td><code class="language-plaintext highlighter-rouge">PUSH 2</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">01 00 00 00 03</code></td>
      <td><code class="language-plaintext highlighter-rouge">PUSH 3</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">2B</code></td>
      <td><code class="language-plaintext highlighter-rouge">ADD</code></td>
    </tr>
  </tbody>
</table>

<p>Now we need a fairly simple program to read that binary code and actually execute it.
The main loop is quite simple:</p>

<ol>
  <li>Read one byte.</li>
  <li>If it’s a <code class="language-plaintext highlighter-rouge">PUSH</code> instruction, read the next four bytes as an integer and push it onto the operand stack.</li>
  <li>If it’s an arithmetic instruction such as <code class="language-plaintext highlighter-rouge">ADD</code>, pop the top two integers from the stack, perform the operation, and push the result back onto the stack.</li>
  <li>If there are no more IR bytes to read, return the top value from the stack and terminate.</li>
  <li>Go to step 1.</li>
</ol>

<p>To visualize this, here’s what the operand stack looks like after each instruction:</p>

<table>
  <thead>
    <tr>
      <th>Instruction</th>
      <th>Stack after execution</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">PUSH 2</code></td>
      <td><code class="language-plaintext highlighter-rouge">2</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">PUSH 3</code></td>
      <td><code class="language-plaintext highlighter-rouge">2</code>, <code class="language-plaintext highlighter-rouge">3</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">ADD</code></td>
      <td><code class="language-plaintext highlighter-rouge">5</code></td>
    </tr>
  </tbody>
</table>

<h2 id="core-loop">Core loop</h2>

<p>Stripping all error handling code and edge cases, this is how the main loop of our VM looks like:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
</pre></td><td class="rouge-code"><pre><span class="k">var</span> <span class="n">stack</span> <span class="p">[]</span><span class="kt">int32</span>
<span class="k">for</span> <span class="n">ip</span> <span class="o">:=</span> <span class="m">0</span><span class="p">;</span> <span class="n">ip</span> <span class="o">&lt;</span> <span class="nb">len</span><span class="p">(</span><span class="n">program</span><span class="p">);</span> <span class="p">{</span>
  <span class="n">opcode</span> <span class="o">:=</span> <span class="n">program</span><span class="p">[</span><span class="n">ip</span><span class="p">]</span>
  <span class="n">ip</span><span class="o">++</span>

  <span class="k">if</span> <span class="n">opcode</span> <span class="o">==</span> <span class="n">pushOpcode</span> <span class="p">{</span>
    <span class="n">stack</span> <span class="o">=</span> <span class="nb">append</span><span class="p">(</span><span class="n">stack</span><span class="p">,</span> <span class="kt">int32</span><span class="p">(</span><span class="n">binary</span><span class="o">.</span><span class="n">BigEndian</span><span class="o">.</span><span class="n">Uint32</span><span class="p">(</span><span class="n">program</span><span class="p">[</span><span class="n">ip</span><span class="o">:</span><span class="n">ip</span><span class="o">+</span><span class="m">4</span><span class="p">])))</span>
    <span class="n">ip</span> <span class="o">+=</span> <span class="m">4</span>
    <span class="k">continue</span>
  <span class="p">}</span>

  <span class="n">right</span> <span class="o">:=</span> <span class="n">stack</span><span class="p">[</span><span class="nb">len</span><span class="p">(</span><span class="n">stack</span><span class="p">)</span><span class="o">-</span><span class="m">1</span><span class="p">]</span>
  <span class="n">left</span> <span class="o">:=</span> <span class="n">stack</span><span class="p">[</span><span class="nb">len</span><span class="p">(</span><span class="n">stack</span><span class="p">)</span><span class="o">-</span><span class="m">2</span><span class="p">]</span>
  <span class="n">stack</span> <span class="o">=</span> <span class="n">stack</span><span class="p">[</span><span class="o">:</span><span class="nb">len</span><span class="p">(</span><span class="n">stack</span><span class="p">)</span><span class="o">-</span><span class="m">2</span><span class="p">]</span>

  <span class="k">var</span> <span class="n">result</span> <span class="kt">int32</span>
  <span class="k">switch</span> <span class="n">opcode</span> <span class="p">{</span>
  <span class="k">case</span> <span class="sc">'+'</span><span class="o">:</span>
    <span class="n">result</span> <span class="o">=</span> <span class="n">left</span> <span class="o">+</span> <span class="n">right</span>
  <span class="k">case</span> <span class="sc">'-'</span><span class="o">:</span>
    <span class="n">result</span> <span class="o">=</span> <span class="n">left</span> <span class="o">-</span> <span class="n">right</span>
  <span class="k">case</span> <span class="sc">'*'</span><span class="o">:</span>
    <span class="n">result</span> <span class="o">=</span> <span class="n">left</span> <span class="o">*</span> <span class="n">right</span>
  <span class="k">case</span> <span class="sc">'/'</span><span class="o">:</span>
    <span class="n">result</span> <span class="o">=</span> <span class="n">left</span> <span class="o">/</span> <span class="n">right</span>
  <span class="p">}</span>
  <span class="n">stack</span> <span class="o">=</span> <span class="nb">append</span><span class="p">(</span><span class="n">stack</span><span class="p">,</span> <span class="n">result</span><span class="p">)</span>
<span class="p">}</span>

<span class="k">return</span> <span class="n">stack</span><span class="p">[</span><span class="m">0</span><span class="p">],</span> <span class="no">nil</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">ip</code> stands for <em>instruction pointer</em>: it’s an index into the byte array containing the IR code.
You can see how we move through the program, either pushing operands onto the stack or popping them to compute a result.
This simplified loop assumes valid bytecode, enough operands for every operation, and exactly one result on the stack.
It might not be clear why we use this unusual postfix notation (number, number, operator) rather than infix notation, such as <code class="language-plaintext highlighter-rouge">2 + 3</code>.
Be patient, you’ll soon realize how powerful such notation is.</p>

<h2 id="taking-our-vm-for-a-test-drive">Taking our VM for a test drive</h2>

<p>The source code above is incomplete.
As usual, you’ll find the complete <a href="https://github.com/nurkiewicz/writing-compiler/tree/part-iv">program on GitHub</a> (branch <code class="language-plaintext highlighter-rouge">part-iv</code>).
But rather than inspecting every <code class="language-plaintext highlighter-rouge">err != nil</code>, let’s run some code!</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre><span class="nv">$ </span><span class="nb">echo</span> <span class="s1">'2 + 3'</span> | ./compiler | ./vm
5
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Or, if you want to take it step-by-step:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre><span class="nv">$ </span><span class="nb">echo</span> <span class="s1">'2 + 3'</span> <span class="o">&gt;</span> file.pl0
<span class="nv">$ </span><span class="nb">cat </span>file.p10 | ./compiler <span class="o">&gt;</span> file.ir
<span class="nv">$ </span><span class="nb">cat </span>file.ir | ./vm 
5
</pre></td></tr></tbody></table></code></pre></div></div>

<p>In a sense, <code class="language-plaintext highlighter-rouge">file.ir</code> is our executable: it contains instructions for a virtual machine rather than for a physical CPU.
The operating system cannot execute it directly; our <code class="language-plaintext highlighter-rouge">vm</code> process must load and interpret it.
JVM <code class="language-plaintext highlighter-rouge">.class</code> files and .NET assemblies follow the same general model.</p>

<h2 id="the-vm-outgrows-the-source-language">The VM outgrows the source language</h2>

<p>We accidentally created an interesting feature in our VM.
It isn’t limited to simple <code class="language-plaintext highlighter-rouge">number op number</code> expressions.
For example, our source language doesn’t support (yet) adding three numbers, like so: <code class="language-plaintext highlighter-rouge">1 + 2 + 3</code>.
But the VM is perfectly capable of executing the corresponding IR!</p>

<table>
  <thead>
    <tr>
      <th>Instruction</th>
      <th>Stack after execution</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">PUSH 1</code></td>
      <td><code class="language-plaintext highlighter-rouge">1</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">PUSH 2</code></td>
      <td><code class="language-plaintext highlighter-rouge">1</code>, <code class="language-plaintext highlighter-rouge">2</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">PUSH 3</code></td>
      <td><code class="language-plaintext highlighter-rouge">1</code>, <code class="language-plaintext highlighter-rouge">2</code>, <code class="language-plaintext highlighter-rouge">3</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">ADD</code></td>
      <td><code class="language-plaintext highlighter-rouge">1</code>, <code class="language-plaintext highlighter-rouge">5</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">ADD</code></td>
      <td><code class="language-plaintext highlighter-rouge">6</code></td>
    </tr>
  </tbody>
</table>

<p>Because our compiler doesn’t support such expressions, we need to construct the binary IR code by hand.
That’s fairly simple; we end up with the following <code class="language-plaintext highlighter-rouge">.ir</code> file:</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre>$ xxd -u -g1 -c 32 add.ir
00000000: 50 4C 2F 30 00 01 01 00 00 00 01 01 00 00 00 02 01 00 00 00 03 2B 2B
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Let’s break it down, instruction by instruction:</p>

<table>
  <thead>
    <tr>
      <th>Binary</th>
      <th>Instruction</th>
      <th>Operand stack after executing</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>50 4C 2F 30 00 01</td>
      <td>Header</td>
      <td> </td>
    </tr>
    <tr>
      <td>01 00 00 00 01</td>
      <td><code class="language-plaintext highlighter-rouge">PUSH 1</code></td>
      <td><code class="language-plaintext highlighter-rouge">1</code></td>
    </tr>
    <tr>
      <td>01 00 00 00 02</td>
      <td><code class="language-plaintext highlighter-rouge">PUSH 2</code></td>
      <td><code class="language-plaintext highlighter-rouge">1</code>, <code class="language-plaintext highlighter-rouge">2</code></td>
    </tr>
    <tr>
      <td>01 00 00 00 03</td>
      <td><code class="language-plaintext highlighter-rouge">PUSH 3</code></td>
      <td><code class="language-plaintext highlighter-rouge">1</code>, <code class="language-plaintext highlighter-rouge">2</code>, <code class="language-plaintext highlighter-rouge">3</code></td>
    </tr>
    <tr>
      <td>2B</td>
      <td><code class="language-plaintext highlighter-rouge">ADD</code></td>
      <td><code class="language-plaintext highlighter-rouge">1</code>, <code class="language-plaintext highlighter-rouge">5</code></td>
    </tr>
    <tr>
      <td>2B</td>
      <td><code class="language-plaintext highlighter-rouge">ADD</code></td>
      <td><code class="language-plaintext highlighter-rouge">6</code></td>
    </tr>
  </tbody>
</table>

<p>Let’s run this file to prove the VM is capable of loading it:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre><span class="nv">$ </span><span class="nb">cat </span>add.ir | ./vm
6
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Amazing!
Our bytecode can already express more than the source language.
OK, but maybe you are still not convinced why our instructions use this unusual <em>Reverse Polish Notation</em> (RPN)?
What’s the point of having an operand stack and operators at the end?
Who the hell writes <code class="language-plaintext highlighter-rouge">1 + 2 + 3</code> as <code class="language-plaintext highlighter-rouge">1 2 3 + +</code>?
Stack-based virtual machines do, as do users of <a href="https://lestallion.com/blogs/product-reviews/best-rpn-calculators-for-engineers-and-scientists">many real calculators</a>.
Clojure’s <code class="language-plaintext highlighter-rouge">(+ 1 2 3)</code> may look similar as well, but it uses prefix notation rather than RPN.</p>

<h2 id="handling-arbitrary-arithmetic-expressions">Handling arbitrary arithmetic expressions</h2>

<p>Our language is definitely not ready for them, but what about expressions like <code class="language-plaintext highlighter-rouge">2 * 3 + 4</code>?
Or <code class="language-plaintext highlighter-rouge">2 * (3 + 4)</code> (parentheses!), or even <code class="language-plaintext highlighter-rouge">2 + 3 * 4</code> (operator precedence!)?
It turns out that our VM is already capable of evaluating arbitrarily complex expressions composed of integer literals and its four arithmetic operators.
Let’s take <code class="language-plaintext highlighter-rouge">2 * 3 + 4</code> as an example.
The following instructions compute it just fine:</p>

<table>
  <thead>
    <tr>
      <th>Instruction</th>
      <th>Stack after execution</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">PUSH 2</code></td>
      <td><code class="language-plaintext highlighter-rouge">2</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">PUSH 3</code></td>
      <td><code class="language-plaintext highlighter-rouge">2</code>, <code class="language-plaintext highlighter-rouge">3</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">MUL</code></td>
      <td><code class="language-plaintext highlighter-rouge">6</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">PUSH 4</code></td>
      <td><code class="language-plaintext highlighter-rouge">6</code>, <code class="language-plaintext highlighter-rouge">4</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">ADD</code></td>
      <td><code class="language-plaintext highlighter-rouge">10</code></td>
    </tr>
  </tbody>
</table>

<p>What if we add parentheses, turning <code class="language-plaintext highlighter-rouge">2 * 3 + 4</code> into <code class="language-plaintext highlighter-rouge">2 * (3 + 4)</code>?
No worries: the bytecode does not need to encode parentheses or precedence explicitly.
The compiler represents the intended evaluation order simply by arranging the instructions:</p>

<table>
  <thead>
    <tr>
      <th>Instruction</th>
      <th>Stack after execution</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">PUSH 2</code></td>
      <td><code class="language-plaintext highlighter-rouge">2</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">PUSH 3</code></td>
      <td><code class="language-plaintext highlighter-rouge">2</code>, <code class="language-plaintext highlighter-rouge">3</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">PUSH 4</code></td>
      <td><code class="language-plaintext highlighter-rouge">2</code>, <code class="language-plaintext highlighter-rouge">3</code>, <code class="language-plaintext highlighter-rouge">4</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">ADD</code></td>
      <td><code class="language-plaintext highlighter-rouge">2</code>, <code class="language-plaintext highlighter-rouge">7</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">MUL</code></td>
      <td><code class="language-plaintext highlighter-rouge">14</code></td>
    </tr>
  </tbody>
</table>

<p>I’ll leave <code class="language-plaintext highlighter-rouge">2 + 3 * 4</code> as an exercise for the reader.
To illustrate how the same convention extends to variables, let’s represent <code class="language-plaintext highlighter-rouge">b^2 - 4ac</code> symbolically.
Our current VM cannot load variables yet, so treat <code class="language-plaintext highlighter-rouge">PUSH a</code>, <code class="language-plaintext highlighter-rouge">PUSH b</code>, and <code class="language-plaintext highlighter-rouge">PUSH c</code> below as placeholders for future load instructions:</p>

<table>
  <thead>
    <tr>
      <th>Instruction</th>
      <th>Stack after execution</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">PUSH b</code></td>
      <td><code class="language-plaintext highlighter-rouge">b</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">PUSH b</code></td>
      <td><code class="language-plaintext highlighter-rouge">b</code>, <code class="language-plaintext highlighter-rouge">b</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">MUL</code></td>
      <td><code class="language-plaintext highlighter-rouge">b^2</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">PUSH 4</code></td>
      <td><code class="language-plaintext highlighter-rouge">b^2</code>, <code class="language-plaintext highlighter-rouge">4</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">PUSH a</code></td>
      <td><code class="language-plaintext highlighter-rouge">b^2</code>, <code class="language-plaintext highlighter-rouge">4</code>, <code class="language-plaintext highlighter-rouge">a</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">PUSH c</code></td>
      <td><code class="language-plaintext highlighter-rouge">b^2</code>, <code class="language-plaintext highlighter-rouge">4</code>, <code class="language-plaintext highlighter-rouge">a</code>, <code class="language-plaintext highlighter-rouge">c</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">MUL</code></td>
      <td><code class="language-plaintext highlighter-rouge">b^2</code>, <code class="language-plaintext highlighter-rouge">4</code>, <code class="language-plaintext highlighter-rouge">a*c</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">MUL</code></td>
      <td><code class="language-plaintext highlighter-rouge">b^2</code>, <code class="language-plaintext highlighter-rouge">4*a*c</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">SUB</code></td>
      <td><code class="language-plaintext highlighter-rouge">b^2 - 4*a*c</code></td>
    </tr>
  </tbody>
</table>

<p>As you can see, this awkward postfix notation lets bytecode encode the evaluation order without retaining parentheses or precedence rules.
Those rules still matter when the compiler parses the source expression, but the VM no longer needs to know about them.
Moreover, we can imagine other programming languages compiling to our admittedly primitive IR.
Supporting multiple source languages is one major advantage of mature runtime platforms such as the JVM and .NET.
We’ll leave compiling to an existing virtual machine for the next part.</p>

<h2 id="write-yourself-a-compiler-series">Write yourself a compiler series</h2>

<ol>
  
    
    <li>
      
        <a href="/2026/07/simplest-interpreter-write-yourself-a-compiler-part-i.html">The simplest interpreter</a>
      
    </li>
  
    
    <li>
      
        <a href="/2026/07/arithmetic-interpreter-write-yourself-a-compiler.html">Arithmetic interpreter</a>
      
    </li>
  
    
    <li>
      
        <a href="/2026/08/compiling-to-intermediate-representation-write-yourself-a-compiler.html">Compiling to intermediate representation</a>
      
    </li>
  
    
    <li>
      
        <strong><a href="/2026/08/your-first-virtual-machine-write-yourself-a-compiler.html">Your First Virtual Machine</a></strong>
      
    </li>
  
    
    <li>
      
        <a href="/2026/09/generating-java-bytecode-write-yourself-a-compiler.html">Generating Java bytecode</a>
      
    </li>
  
    
    <li>
      
        <a href="/2026/09/interoperability-with-java-write-yourself-a-compiler.html">Interoperability with Java</a>
      
    </li>
  
</ol>]]></content><author><name></name></author><category term="writing-compiler" /><category term="compiler" /><category term="interpreter" /><category term="go" /><category term="virtual-machine" /><category term="clojure" /><category term="reverse-polish-notation" /><summary type="html"><![CDATA[In the previous article, we emitted an intermediate representation (IR) for our programming language that is easier to process than source code. However, we did not build a program that could read and execute that IR. Such a program is called a virtual machine. Technically, it’s still an interpreter. But instead of interpreting source code, it interprets IR. Our IR is binary, compact, structured, and generally faster to interpret than the original source. Moreover, as you’ll see later, the VM’s instruction set can express programs that our source language cannot produce yet!]]></summary></entry><entry><title type="html">Compiling to intermediate representation: Write yourself a compiler, Part III</title><link href="https://nurkiewicz.com/2026/08/compiling-to-intermediate-representation-write-yourself-a-compiler.html" rel="alternate" type="text/html" title="Compiling to intermediate representation: Write yourself a compiler, Part III" /><published>2026-08-17T00:00:00+02:00</published><updated>2026-08-17T00:00:00+02:00</updated><id>https://nurkiewicz.com/2026/08/compiling-to-intermediate-representation-write-yourself-a-compiler</id><content type="html" xml:base="https://nurkiewicz.com/2026/08/compiling-to-intermediate-representation-write-yourself-a-compiler.html"><![CDATA[<p>It’s time to dive a bit deeper and abandon the naive realm of interpreters.
Our tiny little project can finally call itself a <em>compiler</em>.
In this part we’ll emit so-called <em>intermediate representation</em> instead of just evaluating and running the source code as-is.
OK, what does this all mean?</p>

<h2 id="intermediate-representation">Intermediate representation</h2>

<p>Languages like JavaScript or Python are traditionally classified as <em>interpreted</em>.
You “run” the source code, there’s no compilation step.
Well, that’s not entirely true.
Python is technically compiled to <em>bitcode</em> - a low-level, binary encoding of source code.
You can find bitcode in <code class="language-plaintext highlighter-rouge">.pyc</code> files somewhere around your project.
On the other hand languages like Java or C# are always compiled, but also to some intermediate representation.
E.g. <a href="https://en.wikipedia.org/wiki/List_of_JVM_bytecode_instructions">bytecode</a> stored in <code class="language-plaintext highlighter-rouge">.class</code> files for Java.
Until recently, you couldn’t just run Java program.
You always had to compile it first.</p>

<p>JavaScript, on the other hand, still interprets the source code.
But under the hood code is JIT-compiled.
JIT stands for <a href="https://en.wikipedia.org/wiki/Just-in-time_compilation">Just-in-time compilation</a>, but that’s a completely different story we might cover later.</p>

<p>That being said, both <code class="language-plaintext highlighter-rouge">.pyc</code> and <code class="language-plaintext highlighter-rouge">.class</code> files aren’t real computer instructions.
They look fairly abstract with instructions like <code class="language-plaintext highlighter-rouge">STORE_SUBSCR</code> and <code class="language-plaintext highlighter-rouge">ldc2_w</code> respectively.
But no computer on earth can run these instructions.
Instead, we build virtual machines <em>interpreting</em> them.</p>

<h2 id="virtual-machine">Virtual machine</h2>

<p>Virtual machine (VM) is just yet another program which can technically be called an <em>interpreter</em>.
But rather than using complex regular expressions and parsing, it interpret intermediate representation (IR).
IR code is typically a bit more verbose, but much easier to interpret.
For example, it’s a linear sequence of commands with no nesting, parentheses, complex flow structures.
It looks closer to assembly, but again, it’s not recognizable by any real CPU.</p>

<p>Instead, VM reads IR instruction-by-instruction and invokes it.
Let me give you a short example in Java.
The expression <code class="language-plaintext highlighter-rouge">2 + 3</code> in Java source code would be translated to:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre>iconst_2
iconst_3
iadd
</pre></td></tr></tbody></table></code></pre></div></div>
<p>In WebAssembly it looks almost exactly the same:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre>i32.const 2
i32.const 3
i32.add
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Of course, such bytecode is encoded in binary, not as text.
The bytecode basically says: push constant <code class="language-plaintext highlighter-rouge">2</code> onto virtual operand stack, followed by <code class="language-plaintext highlighter-rouge">3</code>.
Then the instruction <code class="language-plaintext highlighter-rouge">iadd</code> pops two most recent values from the stack and replaces them with value <code class="language-plaintext highlighter-rouge">5</code>.</p>

<p>This sounds silly, but it’s still way faster than reading through source code.
We’ll talk more about virtual machines in the next part.
We’ll also try to understand what’s the point of generating IR and building a VM rather than, you know, just emitting proper assembly.
Now, it’s time to emit some IR!</p>

<h2 id="generating-ir-from-source-code">Generating IR from source code</h2>

<p>The core principle of intermediate representation is to reduce the source code into format more easily digestable by machine.
In our case we barely need a handful of instructions, sometimes called opcodes:</p>

<table>
  <thead>
    <tr>
      <th>Opcode</th>
      <th>Hex</th>
      <th>Meaning</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">PUSH</code></td>
      <td>01</td>
      <td>Push 32-but number following this opcode onto the stack</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">ADD</code></td>
      <td>2B</td>
      <td>Add</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">SUB</code></td>
      <td>2D</td>
      <td>Subtract</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">MUL</code></td>
      <td>2A</td>
      <td>Multiply</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">DIV</code></td>
      <td>2F</td>
      <td>Divide</td>
    </tr>
  </tbody>
</table>

<p>We have one <code class="language-plaintext highlighter-rouge">PUSH</code> instruction which pushes (duh!) one number onto virtual operand stack.
Theoretically, that stack is infinite.
The remaining four opcodes decide what to do with two top-most numbers on the stack.
Bonus question: why did I choose such random hex codes, like <code class="language-plaintext highlighter-rouge">2B</code>, <code class="language-plaintext highlighter-rouge">2D</code>, etc.?
Never mind, in order to encode <code class="language-plaintext highlighter-rouge">2 + 3</code> expression in our IR, we need the following opcodes:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre>PUSH 2
PUSH 3
ADD
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Where’s the result?
All arithmetic opcodes pop two top-most numbers from the stack and push the result back.
So, after running this program our stack will contain just one entry: number <code class="language-plaintext highlighter-rouge">5</code>.
You might not see the beauty of this notation (called <a href="https://en.wikipedia.org/wiki/Reverse_Polish_notation"><em>Reverse Polish Notation</em></a>), but it’ll become clear once we actually implement the virtual machine.</p>

<h2 id="compiler-implementation">Compiler implementation</h2>

<p>Now it’s time to turn human-readable source code (e.g. <code class="language-plaintext highlighter-rouge">2 + 3</code>) into bytecode.
Or, to be more precise, to the following sequence of bytes:</p>

<table>
  <thead>
    <tr>
      <th>Bytes</th>
      <th>Explanation</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">01 00 00 00 02</code></td>
      <td><code class="language-plaintext highlighter-rouge">PUSH 2</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">01 00 00 00 03</code></td>
      <td><code class="language-plaintext highlighter-rouge">PUSH 3</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">2B</code></td>
      <td><code class="language-plaintext highlighter-rouge">ADD</code></td>
    </tr>
  </tbody>
</table>

<p>Two conventions: I’m using Big Endian for integers (for no apparent reason) and I’ll add a small header with magic value <a href="https://en.wikipedia.org/wiki/PL/0"><code class="language-plaintext highlighter-rouge">PL/0</code></a> and version 0.1 of the format.
Magic value at the beginning of the file is always a good idea to quickly identify what kind of binary we deal with.
Version is useful to support compatibility and quickly discover no longer (or not yet!) supported features of the language.</p>

<p>OK, finally, this is how the core parts of the implementation look like.
Let’s start with the basic data structure describing our entire program:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="rouge-code"><pre><span class="k">type</span> <span class="n">expression</span> <span class="k">struct</span> <span class="p">{</span>
	<span class="n">left</span>  <span class="kt">int32</span>
	<span class="n">op</span>    <span class="kt">byte</span>
	<span class="n">right</span> <span class="kt">int32</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Then, let’s write a routine for turning string expression into <code class="language-plaintext highlighter-rouge">expression</code> instance:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
</pre></td><td class="rouge-code"><pre><span class="k">func</span> <span class="n">parse</span><span class="p">(</span><span class="n">line</span> <span class="kt">string</span><span class="p">)</span> <span class="p">(</span><span class="n">expression</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">matches</span> <span class="o">:=</span> <span class="n">exprRegex</span><span class="o">.</span><span class="n">FindStringSubmatch</span><span class="p">(</span><span class="n">line</span><span class="p">)</span>
	<span class="k">if</span> <span class="n">matches</span> <span class="o">==</span> <span class="no">nil</span> <span class="p">{</span>
		<span class="c">//syntax error</span>
	<span class="p">}</span>

	<span class="n">left</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">parseInt32</span><span class="p">(</span><span class="n">matches</span><span class="p">[</span><span class="m">1</span><span class="p">])</span>
	<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
		<span class="k">return</span> <span class="n">expression</span><span class="p">{},</span> <span class="n">err</span>
	<span class="p">}</span>
	<span class="n">right</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">parseInt32</span><span class="p">(</span><span class="n">matches</span><span class="p">[</span><span class="m">3</span><span class="p">])</span>
	<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
		<span class="k">return</span> <span class="n">expression</span><span class="p">{},</span> <span class="n">err</span>
	<span class="p">}</span>

	<span class="k">return</span> <span class="n">expression</span><span class="p">{</span><span class="n">left</span><span class="o">:</span> <span class="n">left</span><span class="p">,</span> <span class="n">op</span><span class="o">:</span> <span class="n">matches</span><span class="p">[</span><span class="m">2</span><span class="p">][</span><span class="m">0</span><span class="p">],</span> <span class="n">right</span><span class="o">:</span> <span class="n">right</span><span class="p">},</span> <span class="no">nil</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>As you can see we no longer mix parsing with execution.
We have proper data structure and separation of concerns!
The last part is just printing binary-encoded opcodes defined in <code class="language-plaintext highlighter-rouge">expression</code> type:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
</pre></td><td class="rouge-code"><pre><span class="k">var</span> <span class="n">header</span> <span class="o">=</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">{</span><span class="sc">'P'</span><span class="p">,</span> <span class="sc">'L'</span><span class="p">,</span> <span class="sc">'/'</span><span class="p">,</span> <span class="sc">'0'</span><span class="p">,</span> <span class="m">0x00</span><span class="p">,</span> <span class="m">0x01</span><span class="p">}</span>

<span class="k">func</span> <span class="n">writeProgram</span><span class="p">(</span><span class="n">w</span> <span class="n">io</span><span class="o">.</span><span class="n">Writer</span><span class="p">,</span> <span class="n">expr</span> <span class="n">expression</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
	<span class="n">program</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="kt">byte</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">header</span><span class="p">)</span><span class="o">+</span><span class="m">11</span><span class="p">)</span>
	<span class="n">program</span> <span class="o">=</span> <span class="nb">append</span><span class="p">(</span><span class="n">program</span><span class="p">,</span> <span class="n">header</span><span class="o">...</span><span class="p">)</span>
	<span class="n">program</span> <span class="o">=</span> <span class="n">appendPush</span><span class="p">(</span><span class="n">program</span><span class="p">,</span> <span class="n">expr</span><span class="o">.</span><span class="n">left</span><span class="p">)</span>
	<span class="n">program</span> <span class="o">=</span> <span class="n">appendPush</span><span class="p">(</span><span class="n">program</span><span class="p">,</span> <span class="n">expr</span><span class="o">.</span><span class="n">right</span><span class="p">)</span>
	<span class="n">program</span> <span class="o">=</span> <span class="nb">append</span><span class="p">(</span><span class="n">program</span><span class="p">,</span> <span class="n">expr</span><span class="o">.</span><span class="n">op</span><span class="p">)</span>

	<span class="k">if</span> <span class="n">_</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">w</span><span class="o">.</span><span class="n">Write</span><span class="p">(</span><span class="n">program</span><span class="p">);</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
		<span class="k">return</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Errorf</span><span class="p">(</span><span class="s">"error: write program: %w"</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
	<span class="p">}</span>
	<span class="k">return</span> <span class="no">nil</span>
<span class="p">}</span>

<span class="k">func</span> <span class="n">appendPush</span><span class="p">(</span><span class="n">program</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">,</span> <span class="n">value</span> <span class="kt">int32</span><span class="p">)</span> <span class="p">[]</span><span class="kt">byte</span> <span class="p">{</span>
	<span class="n">program</span> <span class="o">=</span> <span class="nb">append</span><span class="p">(</span><span class="n">program</span><span class="p">,</span> <span class="n">pushOpcode</span><span class="p">)</span>
	<span class="k">return</span> <span class="n">binary</span><span class="o">.</span><span class="n">BigEndian</span><span class="o">.</span><span class="n">AppendUint32</span><span class="p">(</span><span class="n">program</span><span class="p">,</span> <span class="kt">uint32</span><span class="p">(</span><span class="n">value</span><span class="p">))</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>We are too lazy to handle program arguments and files, so our compiler can only read source code from <code class="language-plaintext highlighter-rouge">stdin</code> and output IR to <code class="language-plaintext highlighter-rouge">stdout</code>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre><span class="nv">$ </span><span class="nb">echo</span> <span class="s1">'2 + 3'</span> | ./compiler | xxd <span class="nt">-u</span> <span class="nt">-g1</span> <span class="nt">-c</span> 16

00000000: 50 4C 2F 30   00 01   01 00 00 00 02   01 00 00 00 03  PL/0 .. ..... .....
00000010: 2B                                                     +
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Hopefully, you can recognize two <code class="language-plaintext highlighter-rouge">PUSH</code> opcodes (<code class="language-plaintext highlighter-rouge">01 00 00 00 02</code> and <code class="language-plaintext highlighter-rouge">01 00 00 00 03</code>) as well as the terminating <code class="language-plaintext highlighter-rouge">2B</code> (<code class="language-plaintext highlighter-rouge">+</code> in ASCII).
In the next part we’ll write a simple virtual machine: a program which takes this binary IR and executes it.</p>

<p>As always, the complete source code is <a href="https://github.com/nurkiewicz/writing-compiler/tree/part-iii">available on GitHub</a> (<code class="language-plaintext highlighter-rouge">part-iii</code> branch).</p>

<h2 id="write-yourself-a-compiler-series">Write yourself a compiler series</h2>

<ol>
  
    
    <li>
      
        <a href="/2026/07/simplest-interpreter-write-yourself-a-compiler-part-i.html">The simplest interpreter</a>
      
    </li>
  
    
    <li>
      
        <a href="/2026/07/arithmetic-interpreter-write-yourself-a-compiler.html">Arithmetic interpreter</a>
      
    </li>
  
    
    <li>
      
        <strong><a href="/2026/08/compiling-to-intermediate-representation-write-yourself-a-compiler.html">Compiling to intermediate representation</a></strong>
      
    </li>
  
    
    <li>
      
        <a href="/2026/08/your-first-virtual-machine-write-yourself-a-compiler.html">Your First Virtual Machine</a>
      
    </li>
  
    
    <li>
      
        <a href="/2026/09/generating-java-bytecode-write-yourself-a-compiler.html">Generating Java bytecode</a>
      
    </li>
  
    
    <li>
      
        <a href="/2026/09/interoperability-with-java-write-yourself-a-compiler.html">Interoperability with Java</a>
      
    </li>
  
</ol>]]></content><author><name></name></author><category term="writing-compiler" /><category term="compiler" /><category term="interpreter" /><category term="go" /><category term="ir" /><category term="python" /><category term="javascript" /><summary type="html"><![CDATA[It’s time to dive a bit deeper and abandon the naive realm of interpreters. Our tiny little project can finally call itself a compiler. In this part we’ll emit so-called intermediate representation instead of just evaluating and running the source code as-is. OK, what does this all mean?]]></summary></entry><entry><title type="html">mastodon.social Archive</title><link href="https://nurkiewicz.com/2026/07/mastodon.social-archive.html" rel="alternate" type="text/html" title="mastodon.social Archive" /><published>2026-07-23T00:00:00+02:00</published><updated>2026-07-23T00:00:00+02:00</updated><id>https://nurkiewicz.com/2026/07/mastodon.social-archive</id><content type="html" xml:base="https://nurkiewicz.com/2026/07/mastodon.social-archive.html"><![CDATA[<p>This entire page was generated using a script that can be found here: <a href="https://github.com/nurkiewicz/nurkiewicz.com/tree/master/src/mastodon-import"><code class="language-plaintext highlighter-rouge">github.com/nurkiewicz/nurkiewicz.com/tree/master/src/mastodon-import</code></a>.
The script takes export from any Mastodon server (in ZIP format) and turns it into a markdown page.</p>

<hr />

<p><span id="mastodon-109383671763269699"></span></p>

<p>Ep #91 of <a href="https://mastodon.social/tags/aroundITin256">#aroundITin256</a> podcast: Asynchronous communication: loose coupling in distributed systems: <a href="https://nurkiewicz.com/91">https://nurkiewicz.com/91</a></p>

<p>“There are two main ways to communicate between components in your distributed system: synchronous and asynchronous. Synchronous communication is like making a phone call. The system on the other side must be present and you actively wait for a response to your every question. Examples of this style include <a href="https://mastodon.social/tags/REST">#REST</a>, <a href="https://mastodon.social/tags/SOAP">#SOAP</a> and <a href="https://mastodon.social/tags/GraphQL">#GraphQL</a>”</p>

<p><a href="https://mastodon.social/@nurkiewicz/109383671763269699">Mon Nov 21 20:26:14 2022</a> <a href="#mastodon-109383671763269699" title="Permalink to this post">#</a></p>

<hr />

<p><span id="mastodon-109365143464648452"></span></p>

<p>How to read a live-stream of posts on <a href="https://mastodon.social/tags/Mastodon">#Mastodon</a> using <a href="https://mastodon.social/tags/Java">#Java</a> <a href="https://mastodon.social/tags/Spring">#Spring</a> and <a href="https://mastodon.social/tags/WebFlux">#WebFlux</a>. About 5-10 posts per second</p>

<p><img src="/img/mastodon.social/109365143464648452-814609230073501a.png" alt="WebClient.create()
		.get()
		.uri(&quot;https://mastodon.social/api/v1/streaming/public&quot;)
		.retrieve()
		.bodyToFlux(ServerSentEvent.class)
		.subscribe(System.out::println);" /></p>

<p><a href="https://mastodon.social/@nurkiewicz/109365143464648452">Fri Nov 18 13:54:14 2022</a> <a href="#mastodon-109365143464648452" title="Permalink to this post">#</a></p>

<hr />

<p><span id="mastodon-109364905470817625"></span></p>

<p>I once worked at a bank with a very strict firewall. Accessing Facebook or GMail resulted in “This website is blocked. Your manager will be notified”. So one day I found a list of a few thousand most popular websites in a large CSV file. I run a script to check HTTP status of all of them.</p>

<p>Never mind the results, I soon realized I just opened, among others, hundreds of adult websites. On a work computer. Notifying my manager. That I visited tons of XXX sites in a matter of minutes. Using curl</p>

<p><a href="https://mastodon.social/@nurkiewicz/109364905470817625">Fri Nov 18 12:53:43 2022</a> <a href="#mastodon-109364905470817625" title="Permalink to this post">#</a></p>

<hr />

<p><span id="mastodon-109352433733567086"></span></p>

<p>Ep #90 of <a href="https://mastodon.social/tags/aroundITin256">#aroundITin256</a> <a href="https://mastodon.social/tags/podcast">#podcast</a>: <a href="https://mastodon.social/tags/Mastodon">#Mastodon</a>: next-generation, open source social network: <a href="https://nurkiewicz.com/90">https://nurkiewicz.com/90</a></p>

<p>“After many dramatic events around <a href="https://mastodon.social/tags/Twitter">#Twitter</a> lately, many people, including myself, began experimenting with Mastodon. Superficially it’s an open-source clone of the former that anyone can deploy and host. However, once you look deeper, it’s actually a completely different philosophy and architecture. Let’s focus on how Mastodon federated network is built”</p>

<p><a href="https://mastodon.social/@nurkiewicz/109352433733567086">Wed Nov 16 08:01:59 2022</a> <a href="#mastodon-109352433733567086" title="Permalink to this post">#</a></p>

<hr />

<p><span id="mastodon-109349908851833019"></span></p>

<p>11 years of <a href="https://mastodon.social/tags/Twitter">#Twitter</a> posts, 600 tweets and 300 RTs, all archived on my blog: <a href="https://nurkiewicz.com/2022/11/Twitter-archive.html">https://nurkiewicz.com/2022/11/Twitter-archive.html</a></p>

<p>Thank you <a href="https://mathstodon.xyz/@timhutton">@timhutton</a> for your <a href="https://github.com/timhutton/twitter-archive-parser">https://github.com/timhutton/twitter-archive-parser</a></p>

<p><a href="https://mastodon.social/@nurkiewicz/109349908851833019">Tue Nov 15 21:19:53 2022</a> <a href="#mastodon-109349908851833019" title="Permalink to this post">#</a></p>

<hr />

<p><span id="mastodon-109348012582195374"></span></p>

<p>@mariuszgil@twitter.com: “Dziś w Better Software Design rozmowa z <a href="https://mastodon.social/@nurkiewicz">@nurkiewicz</a> na temat programowania reaktywnego. Jak działa ten paradygmat, w czym pomaga, a kiedy przyniesie więcej problemów niż korzyści - o tym wszystkim (i nie tylko) można posłuchać w odcinku BSD 44”</p>

<p><a href="https://bettersoftwaredesign.pl/episodes/44">https://bettersoftwaredesign.pl/episodes/44</a></p>

<p><a href="https://mastodon.social/tags/RxJava">#RxJava</a> <a href="https://mastodon.social/tags/Reactor">#Reactor</a> <a href="https://mastodon.social/tags/Reactive">#Reactive</a> <a href="https://mastodon.social/tags/Java">#Java</a> <a href="https://mastodon.social/tags/RxJS">#RxJS</a></p>

<p><a href="https://mastodon.social/@nurkiewicz/109348012582195374">Tue Nov 15 13:17:38 2022</a> <a href="#mastodon-109348012582195374" title="Permalink to this post">#</a></p>

<hr />

<p><span id="mastodon-109344505987894582"></span></p>

<p>The <a href="https://mastodon.social/tags/Netflix">#Netflix</a> Simian Army is growing! <a href="https://mastodon.social/tags/ChaosEngineering">#ChaosEngineering</a> at its best at <a href="https://mastodon.social/tags/Twitter">#Twitter</a>:</p>

<ul>
  <li>Chaos Monkey: randomly disables individual instances</li>
  <li>
    <p>Chaos Gorilla: simulates an outage of an entire availability zone</p>
  </li>
  <li>[new] Chaos CEO: fires half of the stuff, figure out later who was actually needed</li>
  <li>[new] Chaos PO: ships half-baked features with impossible deadlines; rolls back hour later</li>
  <li>[new] Chaos ego: “turning off the ‘microservices’ bloatware. Less than 20% are actually needed”</li>
</ul>

<p><a href="https://mastodon.social/@nurkiewicz/109344505987894582">Mon Nov 14 22:25:52 2022</a> <a href="#mastodon-109344505987894582" title="Permalink to this post">#</a></p>

<hr />

<p><span id="mastodon-109342688013200791"></span></p>

<p><a href="https://mastodon.social/tags/TIL">#TIL</a> from <a href="https://mastodon.pl/@panprocesor">@panprocesor</a> When extracting nullable INT from ResultSet in <a href="https://mastodon.social/tags/Java">#Java</a> <a href="https://mastodon.social/tags/JDBC">#JDBC</a>, getInt() method returns primitive int. So if column is nullable, you won’t get null but… 0. Extra care must be taken:</p>

<p><img src="/img/mastodon.social/109342688013200791-a298c765840f1620.png" alt="Integer column = resultSet.getInt(&quot;column&quot;);
if (resultSet.wasNull()) {
    column = null;
}" /></p>

<p><a href="https://mastodon.social/@nurkiewicz/109342688013200791">Mon Nov 14 14:43:31 2022</a> <a href="#mastodon-109342688013200791" title="Permalink to this post">#</a></p>

<hr />

<p><span id="mastodon-109341718354708092"></span></p>

<p>“All good APIs are alike; each bad <a href="https://mastodon.social/tags/API">#API</a> is bad in its own way” - Leo Tolstoy</p>

<p><a href="https://mastodon.social/@nurkiewicz/109341718354708092">Mon Nov 14 10:36:56 2022</a> <a href="#mastodon-109341718354708092" title="Permalink to this post">#</a></p>

<hr />

<p><span id="mastodon-109338812541237766"></span></p>

<p>My talk about Project <a href="https://mastodon.social/tags/Loom">#Loom</a> from <a href="https://mastodon.social/tags/Jetbrains">#Jetbrains</a> <a href="https://mastodon.social/tags/Intellij">#Intellij</a> <a href="https://mastodon.social/tags/IDEA">#IDEA</a> Conf is online: <a href="https://www.youtube.com/watch?v=0DUlUzqr09I">https://www.youtube.com/watch?v=0DUlUzqr09I</a> <a href="https://mastodon.social/tags/Java">#Java</a></p>

<p><a href="https://mastodon.social/@nurkiewicz/109338812541237766">Sun Nov 13 22:17:56 2022</a> <a href="#mastodon-109338812541237766" title="Permalink to this post">#</a></p>

<hr />

<p><span id="mastodon-109338691306361475"></span></p>

<p>I found watching this <a href="https://mastodon.social/tags/AI">#AI</a> simulation of predators and preys using <a href="https://mastodon.social/tags/artificialNeuralNetwork">#artificialNeuralNetwork</a> pretty mesmerizing: <a href="https://www.youtube.com/watch?v=tVNoetVLuQg">https://www.youtube.com/watch?v=tVNoetVLuQg</a>. At least check out video after 19th minute</p>

<p><a href="https://mastodon.social/@nurkiewicz/109338691306361475">Sun Nov 13 21:47:07 2022</a> <a href="#mastodon-109338691306361475" title="Permalink to this post">#</a></p>

<hr />

<p><span id="mastodon-109327115874499280"></span></p>

<p><a href="https://mastodon.social/tags/TIL">#TIL</a>: What do the: “two of the world’s deadliest disasters in modern human history: lead poisoning and the depletion of the ozone layer” have in common? Both leaded gasoline and Freon were invented by the same guy, Thomas Midgley Jr.</p>

<p>How he died? “He devised an elaborate system of ropes and pulleys to lift himself out of bed. In 1944, he became entangled in the device and died of strangulation”. Quotes from <a href="https://mastodon.social/tags/Wikipedia">#Wikipedia</a>: <a href="https://en.wikipedia.org/wiki/Thomas_Midgley_Jr">https://en.wikipedia.org/wiki/Thomas_Midgley_Jr</a>. Also, check out <a href="https://www.pushkin.fm/podcasts/cautionary-tales/the-inventor-who-almost-ended-the-world">https://www.pushkin.fm/podcasts/cautionary-tales/the-inventor-who-almost-ended-the-world</a> <a href="https://mastodon.social/tags/podcast">#podcast</a></p>

<p><a href="https://mastodon.social/@nurkiewicz/109327115874499280">Fri Nov 11 20:43:19 2022</a> <a href="#mastodon-109327115874499280" title="Permalink to this post">#</a></p>

<hr />

<p><span id="mastodon-109320323903140265"></span></p>

<p>Extremely useful <a href="https://mastodon.social/tags/Linux">#Linux</a> <a href="https://mastodon.social/tags/Bash">#Bash</a> knowledge base: <a href="https://github.com/jlevy/the-art-of-command-line">https://github.com/jlevy/the-art-of-command-line</a>. I love how dense it is. Just pointers to things you should be aware of, rather than complete <a href="https://mastodon.social/tags/tutorial">#tutorial</a></p>

<p><a href="https://mastodon.social/@nurkiewicz/109320323903140265">Thu Nov 10 15:56:02 2022</a> <a href="#mastodon-109320323903140265" title="Permalink to this post">#</a></p>

<hr />

<p><span id="mastodon-109318770528819069"></span></p>

<p>In <a href="https://mastodon.social/tags/Slack">#Slack</a> “When you’ve sent a message and notice it’s not quite right, you can unsend it to fix your typo […] and then send it off when it’s truly ready. Unsending messages will remove the message from the channel and return the content, including files, to the message input, where you can edit the message to resend or delete it.”</p>

<p>Use ⌘+Z or Ctrl+Z. See <a href="https://slack.com/help/articles/202395258-Edit-or-delete-messages#unsend-a-message">https://slack.com/help/articles/202395258-Edit-or-delete-messages#unsend-a-message</a></p>

<p>More <a href="https://mastodon.social/tags/Slack">#Slack</a> tips: <a href="https://nurkiewicz.com/2022/01/slack-pro-tips.html">https://nurkiewicz.com/2022/01/slack-pro-tips.html</a></p>

<p><a href="https://mastodon.social/@nurkiewicz/109318770528819069">Thu Nov 10 09:21:00 2022</a> <a href="#mastodon-109318770528819069" title="Permalink to this post">#</a></p>

<hr />

<p><span id="mastodon-109318723767588582"></span></p>

<p>Hofstadter’s Law: It always takes longer than you expect, even when you take into account Hofstadter’s Law</p>

<p><a href="https://mastodon.social/@nurkiewicz/109318723767588582">Thu Nov 10 09:09:06 2022</a> <a href="#mastodon-109318723767588582" title="Permalink to this post">#</a></p>

<hr />

<p><span id="mastodon-109315654013103229"></span></p>

<p>Replying to <a href="https://fosstodon.org/users/moelholm/statuses/109315117083469208">a Mastodon post</a></p>

<p><a href="https://fosstodon.org/@moelholm">@moelholm</a> Check out <a href="https://javabubble.org">https://javabubble.org</a> by <a href="https://mastodon.social/@marcandsweep">@marcandsweep</a>. Currently 140 <a href="https://mastodon.social/tags/Java">#Java</a> gurus, ready to batch import via CSV :-)</p>

<p><a href="https://mastodon.social/@nurkiewicz/109315654013103229">Wed Nov 09 20:08:25 2022</a> <a href="#mastodon-109315654013103229" title="Permalink to this post">#</a></p>

<hr />

<p><span id="mastodon-109314191036159191"></span></p>

<p>Since 2019 I have been collecting <a href="https://mastodon.social/tags/crypto">#crypto</a> scams and fails under <a href="https://github.com/nurkiewicz/crypto-hall-of-shame">https://github.com/nurkiewicz/crypto-hall-of-shame</a>.</p>

<p>However, especially after <a href="https://mastodon.social/tags/NFT">#NFT</a> craze, the number of cases was impossible for me to digest. <a href="https://mastodon.social/@molly0xfff">@molly0xfff</a> with her <a href="https://indieweb.social/@web3isgreat">@web3isgreat</a> is doing much better work 😉</p>

<p><a href="https://mastodon.social/@nurkiewicz/109314191036159191">Wed Nov 09 13:56:22 2022</a> <a href="#mastodon-109314191036159191" title="Permalink to this post">#</a></p>

<hr />

<p><span id="mastodon-109307582094941628"></span></p>

<p>In <a href="https://mastodon.social/tags/Mastodon">#Mastodon</a> you can easily filter out posts with tags or keywords you are not interested. Just add custom filter under Preferences.</p>

<p>Toots will be either hidden completely, or hidden with possibility to show anyway.</p>

<p><img src="/img/mastodon.social/109307582094941628-f8fdf6a7e6346664.png" alt="#Mastodon interface showing a filtered keywords" /></p>

<p><a href="https://mastodon.social/@nurkiewicz/109307582094941628">Tue Nov 08 09:55:38 2022</a> <a href="#mastodon-109307582094941628" title="Permalink to this post">#</a></p>

<hr />

<p><span id="mastodon-109304478260926836"></span></p>

<p><a href="https://mastodon.social/tags/Math">#Math</a> videos by <a href="https://mastodon.social/tags/3blue1brown">#3blue1brown</a> are stunning. Brilliant visuals, fantastic explanations. Every single movie is a <a href="https://mastodon.social/tags/science">#science</a> masterpiece.</p>

<p>For example, check out the last one: <a href="https://www.youtube.com/watch?v=851U557j6HE">https://www.youtube.com/watch?v=851U557j6HE</a>. I never saw Fourier Transformation being explained so nicely!</p>

<p><a href="https://mastodon.social/@nurkiewicz/109304478260926836">Mon Nov 07 20:46:17 2022</a> <a href="#mastodon-109304478260926836" title="Permalink to this post">#</a></p>

<hr />

<p><span id="mastodon-109302879774373189"></span></p>

<p><a href="https://mastodon.social/tags/React">#React</a> VFC and FC, less than 1 year apart :-). See also:</p>

<ul>
  <li>
    <p>Migrating <a href="https://mastodon.social/tags/Uber">#Uber</a> from <a href="https://mastodon.social/tags/MySQL">#MySQL</a> to <a href="https://mastodon.social/tags/PostgreSQL">#PostgreSQL</a> (2013: <a href="https://www.yumpu.com/en/document/view/53683323/migrating-uber-from-mysql-to-postgresql">https://www.yumpu.com/en/document/view/53683323/migrating-uber-from-mysql-to-postgresql</a>)</p>
  </li>
  <li>
    <p>Why Uber Engineering Switched from <a href="https://mastodon.social/tags/Postgres">#Postgres</a> to <a href="https://mastodon.social/tags/MySQL">#MySQL</a> (2016: <a href="https://www.uber.com/en-PL/blog/postgres-to-mysql-migration/">https://www.uber.com/en-PL/blog/postgres-to-mysql-migration/</a>)</p>
  </li>
</ul>

<p><img src="/img/mastodon.social/109302879774373189-6a40ee94d8ac129c.png" alt="Google search result showing React.VFC vs. React.FC" /></p>

<p><a href="https://mastodon.social/@nurkiewicz/109302879774373189">Mon Nov 07 13:59:46 2022</a> <a href="#mastodon-109302879774373189" title="Permalink to this post">#</a></p>

<hr />

<p><span id="mastodon-109301921140618535"></span></p>

<p>Replying to <a href="https://mastodon.online/users/philipp_hauer/statuses/109296702497381298">a Mastodon post</a></p>

<p><a href="https://mastodon.online/@philipp_hauer">@philipp_hauer</a> I love <a href="https://mastodon.social/tags/Obsidian">#Obsidian</a> for simple <a href="https://mastodon.social/tags/UX">#UX</a>. Especially the Command Palette (Cmd+P) helps in navigation. For a while I wrote Work Journal every day, but I switched to notes on <a href="https://mastodon.social/tags/GitHub">#GitHub</a>: <a href="https://github.com/nurkiewicz/awesome-notes">https://github.com/nurkiewicz/awesome-notes</a>.</p>

<p>What I use most is looking up relationships between notes, e.g. which one of my colleagues worked for some company or technology.</p>

<p>Things that didn’t work well for me:</p>
<ul>
  <li>simple presentations from markdown</li>
  <li>global knowledge graph is shiny, but not that usable (see image)</li>
</ul>

<p><img src="/img/mastodon.social/109301921140618535-f12afc266cdb7504.png" alt="Obsidian knowledge graph of all notes, without labels" /></p>

<p><a href="https://mastodon.social/@nurkiewicz/109301921140618535">Mon Nov 07 09:55:58 2022</a> <a href="#mastodon-109301921140618535" title="Permalink to this post">#</a></p>

<hr />

<p><span id="mastodon-109293052382323722"></span></p>

<p>Replying to <a href="https://mastodon.social/users/debugagent/statuses/109293028781915604">a Mastodon post</a></p>

<p><a href="https://mastodon.social/@debugagent">@debugagent</a> With URL in <a href="https://mastodon.social/tags/Java">#Java</a>  it gets even better. Two different URLs may be considered equal when you have Internet access, but not equal when disconnected. That’s because DNS unavailability triggers fallback to literal comparison. See also: <a href="https://twitter.com/tnurkiewicz/status/1519643900423688192">https://twitter.com/tnurkiewicz/status/1519643900423688192</a></p>

<p><em>Originally published to a limited audience.</em></p>

<p><a href="https://mastodon.social/@nurkiewicz/109293052382323722">Sat Nov 05 20:20:32 2022</a> <a href="#mastodon-109293052382323722" title="Permalink to this post">#</a></p>

<hr />

<p><span id="mastodon-109290883875044469"></span></p>

<p>“Weeks of coding can save you hours of planning”</p>

<p>Do you know who’s the author of this quote? It seems to be decades old, but I can’t seem to find the source… <a href="https://twitter.com/codewisdom/status/1002181404061552640">https://twitter.com/codewisdom/status/1002181404061552640</a></p>

<p><a href="https://mastodon.social/@nurkiewicz/109290883875044469">Sat Nov 05 11:09:03 2022</a> <a href="#mastodon-109290883875044469" title="Permalink to this post">#</a></p>

<hr />

<p><span id="mastodon-109286276258848826"></span></p>

<p><a href="https://mastodon.social/@jponge">@jponge</a> Check out <a href="https://mastodon.social/@marcandsweep/109264939603607451">https://mastodon.social/@marcandsweep/109264939603607451</a></p>

<p><a href="https://mastodon.social/@nurkiewicz/109286276258848826">Fri Nov 04 15:37:16 2022</a> <a href="#mastodon-109286276258848826" title="Permalink to this post">#</a></p>

<hr />

<p><span id="mastodon-109285567639527047"></span></p>

<p>“Where We’re Going, We Don’t Need Servers!” by @samnewman@twitter.com - very good introduction to serverless and FaaS in particular: <a href="https://www.youtube.com/watch?v=Mz-b-rQ9wL0">https://www.youtube.com/watch?v=Mz-b-rQ9wL0</a></p>

<p><a href="https://mastodon.social/@nurkiewicz/109285567639527047">Fri Nov 04 12:37:04 2022</a> <a href="#mastodon-109285567639527047" title="Permalink to this post">#</a></p>

<hr />

<p><span id="mastodon-109284915651133306"></span></p>

<p>Replying to <a href="https://mastodon.social/users/shmarkus/statuses/109284292928166473">a Mastodon post</a></p>

<p><a href="https://mastodon.social/@shmarkus">@shmarkus</a> One more idea if <a href="https://mastodon.social/tags/TDD">#TDD</a> is too much for someone - never fix a bug without proving there’s a bug in the first place. How? By creating a failing test case first. Now you:</p>

<ul>
  <li>documented a bug</li>
  <li>it’s much easier to fix, guided by test</li>
  <li>code review is simpler</li>
  <li>you don’t risk someone reverting your fix</li>
  <li>you gradually build code coverage</li>
</ul>

<p><a href="https://mastodon.social/@nurkiewicz/109284915651133306">Fri Nov 04 09:51:15 2022</a> <a href="#mastodon-109284915651133306" title="Permalink to this post">#</a></p>

<hr />

<p><span id="mastodon-109275820823348675"></span></p>

<p>Replying to <a href="https://chaos.social/users/yorgos/statuses/109275389831513871">a Mastodon post</a></p>

<p><a href="https://chaos.social/@yorgos">@yorgos</a> Indeed, I can’t imagine working without clipboard manager. My app of choice is <a href="https://tapbots.com/pastebot/">https://tapbots.com/pastebot/</a>, nice and simple</p>

<p><a href="https://mastodon.social/@nurkiewicz/109275820823348675">Wed Nov 02 19:18:19 2022</a> <a href="#mastodon-109275820823348675" title="Permalink to this post">#</a></p>

<hr />

<p><span id="mastodon-109274709792771650"></span></p>

<p>4 <a href="https://mastodon.social/tags/Twitter">#Twitter</a> features <a href="https://mastodon.social/tags/Mastodon">#Mastodon</a> is better for not having (by <a href="https://social.coop/@graue">@graue</a>). These are deliberate <a href="https://mastodon.social/tags/UX">#UX</a> choices:</p>

<ul>
  <li>post metrics aren’t visible at first sight</li>
  <li>notifications when someone likes a reply</li>
  <li>quoting toots</li>
  <li>full-text search</li>
</ul>

<p>See: <a href="https://scott.mn/2022/10/29/twitter_features_mastodon_is_better_without/">https://scott.mn/2022/10/29/twitter_features_mastodon_is_better_without/</a></p>

<p><a href="https://mastodon.social/@nurkiewicz/109274709792771650">Wed Nov 02 14:35:46 2022</a> <a href="#mastodon-109274709792771650" title="Permalink to this post">#</a></p>

<hr />

<p><span id="mastodon-109274231159942191"></span></p>

<p>The importance of unit tests is often underestimated. Failing test case takes seconds to fix. Failing production due to the same bug takes hours of troubleshooting, retesting and deploying. We don’t appreciate how much time we save by having them</p>

<p><a href="https://mastodon.social/@nurkiewicz/109274231159942191">Wed Nov 02 12:34:03 2022</a> <a href="#mastodon-109274231159942191" title="Permalink to this post">#</a></p>

<hr />

<p><span id="mastodon-109259595653594613"></span></p>

<p>Story time. About a decade ago we sent 700 thousand text messages to one person. Why? Sending an SMS was as simple as inserting a record into a special DB table. Some background job was picking it up and forwarding it to the SMS gateway. But for performance reasons, we disabled transaction isolation 😱. Transaction failed and rolled back 700k times. But the temporarily created record was still visible outside, despite rollback. Please, use transactions. And don’t use the database as a queue</p>

<p><a href="https://mastodon.social/@nurkiewicz/109259595653594613">Sun Oct 30 22:32:03 2022</a> <a href="#mastodon-109259595653594613" title="Permalink to this post">#</a></p>]]></content><author><name></name></author><summary type="html"><![CDATA[I migrated from mastodon.social to fosstodon.org. This page is meant to archive all my posts from the old instance, just in case.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://nurkiewicz.com/img/mastodon.social/109307582094941628-f8fdf6a7e6346664.png" /><media:content medium="image" url="https://nurkiewicz.com/img/mastodon.social/109307582094941628-f8fdf6a7e6346664.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Arithmetic interpreter: Write yourself a compiler, Part II</title><link href="https://nurkiewicz.com/2026/07/arithmetic-interpreter-write-yourself-a-compiler.html" rel="alternate" type="text/html" title="Arithmetic interpreter: Write yourself a compiler, Part II" /><published>2026-07-22T00:00:00+02:00</published><updated>2026-07-22T00:00:00+02:00</updated><id>https://nurkiewicz.com/2026/07/arithmetic-interpreter-write-yourself-a-compiler</id><content type="html" xml:base="https://nurkiewicz.com/2026/07/arithmetic-interpreter-write-yourself-a-compiler.html"><![CDATA[<p>In the <a href="/2026/07/simplest-interpreter-write-yourself-a-compiler-part-i.html">previous article</a>, we created the most naive interpreter, which can basically execute <code class="language-plaintext highlighter-rouge">number + number</code> expressions.
A logical extension is obviously to handle all basic operations: addition, subtraction, multiplication and division.
The time has come!</p>

<p>First, we need to teach our proto-compiler how to recognize all basic operators.
This simple code:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="k">const</span> <span class="n">numberPattern</span> <span class="o">=</span> <span class="s">`\s*([+-]?(?:\d+\.?\d*|\.\d+))\s*`</span>

<span class="k">var</span> <span class="n">exprRegex</span> <span class="o">=</span> <span class="n">regexp</span><span class="o">.</span><span class="n">MustCompile</span><span class="p">(</span><span class="s">`^`</span> <span class="o">+</span> <span class="n">numberPattern</span> <span class="o">+</span> <span class="s">`([+])`</span> <span class="o">+</span> <span class="n">numberPattern</span> <span class="o">+</span> <span class="s">`$`</span><span class="p">)</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>becomes:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="k">var</span> <span class="n">exprRegex</span> <span class="o">=</span> <span class="n">regexp</span><span class="o">.</span><span class="n">MustCompile</span><span class="p">(</span><span class="s">`^`</span> <span class="o">+</span> <span class="n">numberPattern</span> <span class="o">+</span> <span class="s">`([+\-*/])`</span> <span class="o">+</span> <span class="n">numberPattern</span> <span class="o">+</span> <span class="s">`$`</span><span class="p">)</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>The code is very similar; the only difference is that the regular expression now supports all sorts of expressions, such as <code class="language-plaintext highlighter-rouge">-2 * 3</code>, <code class="language-plaintext highlighter-rouge">3 - 5</code>, etc.
To break down this regular expression, let’s extract even more primitives:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
</pre></td><td class="rouge-code"><pre><span class="k">const</span> <span class="p">(</span>
	<span class="n">ws</span>       <span class="o">=</span> <span class="s">`\s*`</span>
	<span class="n">number</span>   <span class="o">=</span> <span class="s">`[+-]?\d+`</span>
	<span class="n">operator</span> <span class="o">=</span> <span class="s">`[+\-*/]`</span>
<span class="p">)</span>

<span class="k">func</span> <span class="n">c</span><span class="p">(</span><span class="n">pattern</span> <span class="kt">string</span><span class="p">)</span> <span class="kt">string</span> <span class="p">{</span>
	<span class="k">return</span> <span class="s">`(`</span> <span class="o">+</span> <span class="n">pattern</span> <span class="o">+</span> <span class="s">`)`</span>
<span class="p">}</span>

<span class="k">var</span> <span class="n">exprRegex</span> <span class="o">=</span> <span class="n">regexp</span><span class="o">.</span><span class="n">MustCompile</span><span class="p">(</span><span class="s">`^`</span> <span class="o">+</span> <span class="n">ws</span> <span class="o">+</span> <span class="n">c</span><span class="p">(</span><span class="n">number</span><span class="p">)</span> <span class="o">+</span> <span class="n">ws</span> <span class="o">+</span> <span class="n">c</span><span class="p">(</span><span class="n">operator</span><span class="p">)</span> <span class="o">+</span> <span class="n">ws</span> <span class="o">+</span> <span class="n">c</span><span class="p">(</span><span class="n">number</span><span class="p">)</span> <span class="o">+</span> <span class="n">ws</span> <span class="o">+</span> <span class="s">`$`</span><span class="p">)</span>

</pre></td></tr></tbody></table></code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">exprRegex</code> looks a bit more high-level and better explains what we’re actually trying to achieve.
Only <code class="language-plaintext highlighter-rouge">ws</code> (whitespace) and <code class="language-plaintext highlighter-rouge">c()</code> (<em>capture</em>) leak through the abstraction.</p>

<p>The second necessary change is in the interpreter itself.
It now needs to take the operator into account:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
</pre></td><td class="rouge-code"><pre><span class="k">switch</span> <span class="n">op</span> <span class="p">{</span>
<span class="k">case</span> <span class="sc">'+'</span><span class="o">:</span>
	<span class="k">return</span> <span class="n">a</span> <span class="o">+</span> <span class="n">b</span><span class="p">,</span> <span class="no">nil</span>
<span class="k">case</span> <span class="sc">'-'</span><span class="o">:</span>
	<span class="k">return</span> <span class="n">a</span> <span class="o">-</span> <span class="n">b</span><span class="p">,</span> <span class="no">nil</span>
<span class="k">case</span> <span class="sc">'*'</span><span class="o">:</span>
	<span class="k">return</span> <span class="n">a</span> <span class="o">*</span> <span class="n">b</span><span class="p">,</span> <span class="no">nil</span>
<span class="k">case</span> <span class="sc">'/'</span><span class="o">:</span>
	<span class="k">if</span> <span class="n">b</span> <span class="o">==</span> <span class="m">0</span> <span class="p">{</span>
		<span class="k">return</span> <span class="m">0</span><span class="p">,</span> <span class="n">errors</span><span class="o">.</span><span class="n">New</span><span class="p">(</span><span class="s">"error: division by zero"</span><span class="p">)</span>
	<span class="p">}</span>
	<span class="k">return</span> <span class="n">a</span> <span class="o">/</span> <span class="n">b</span><span class="p">,</span> <span class="no">nil</span>
<span class="k">default</span><span class="o">:</span>
	<span class="k">return</span> <span class="m">0</span><span class="p">,</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Errorf</span><span class="p">(</span><span class="s">"error: unknown operator %q"</span><span class="p">,</span> <span class="kt">string</span><span class="p">(</span><span class="n">op</span><span class="p">))</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>OK, once again, the outcome seems disappointing.
After all, we just parsed a string using a regular expression and, well, interpreted it.
But our tiny programming language is taking shape.
In the next installment, we’ll actually try to “compile” it into an intermediate representation (IR).</p>

<p>The source code is available <a href="https://github.com/nurkiewicz/writing-compiler/tree/part-ii">here</a> on the <code class="language-plaintext highlighter-rouge">part-ii</code> branch.</p>

<h2 id="write-yourself-a-compiler-series">Write yourself a compiler series</h2>

<ol>
  
    
    <li>
      
        <a href="/2026/07/simplest-interpreter-write-yourself-a-compiler-part-i.html">The simplest interpreter</a>
      
    </li>
  
    
    <li>
      
        <strong><a href="/2026/07/arithmetic-interpreter-write-yourself-a-compiler.html">Arithmetic interpreter</a></strong>
      
    </li>
  
    
    <li>
      
        <a href="/2026/08/compiling-to-intermediate-representation-write-yourself-a-compiler.html">Compiling to intermediate representation</a>
      
    </li>
  
    
    <li>
      
        <a href="/2026/08/your-first-virtual-machine-write-yourself-a-compiler.html">Your First Virtual Machine</a>
      
    </li>
  
    
    <li>
      
        <a href="/2026/09/generating-java-bytecode-write-yourself-a-compiler.html">Generating Java bytecode</a>
      
    </li>
  
    
    <li>
      
        <a href="/2026/09/interoperability-with-java-write-yourself-a-compiler.html">Interoperability with Java</a>
      
    </li>
  
</ol>]]></content><author><name></name></author><category term="writing-compiler" /><category term="compiler" /><category term="interpreter" /><category term="go" /><summary type="html"><![CDATA[In the previous article, we created the most naive interpreter, which can basically execute number + number expressions. A logical extension is obviously to handle all basic operations: addition, subtraction, multiplication and division. The time has come!]]></summary></entry><entry><title type="html">The simplest interpreter: Write yourself a compiler, Part I</title><link href="https://nurkiewicz.com/2026/07/simplest-interpreter-write-yourself-a-compiler-part-i.html" rel="alternate" type="text/html" title="The simplest interpreter: Write yourself a compiler, Part I" /><published>2026-07-17T00:00:00+02:00</published><updated>2026-07-17T00:00:00+02:00</updated><id>https://nurkiewicz.com/2026/07/simplest-interpreter-write-yourself-a-compiler-part-i</id><content type="html" xml:base="https://nurkiewicz.com/2026/07/simplest-interpreter-write-yourself-a-compiler-part-i.html"><![CDATA[<p>I’ve always felt that writing a compiler is the most romantic software engineering task.
You’re writing a program that reads textual instructions describing precisely what a computer should do.
Do not confuse this with prompting an LLM, where you write vague, verbose instructions that only loosely describe what a computer might do.
But I digress.</p>

<p>I’m starting a new series of articles in which each part will bring us one tiny step closer to a full-blown compiler.
I’ll make small, incremental improvements in each step.
They’ll be easy to grasp, but each will still add some value.
It’s going to be the most agile compiler ever.</p>

<p>We’ll start by creating an extremely simple interpreter.
It will read a string containing an addition (e.g. <code class="language-plaintext highlighter-rouge">40 + 2</code>), parse it, evaluate it, and print the answer (<code class="language-plaintext highlighter-rouge">42</code>).
This doesn’t sound like a compiler, that’s for sure.
More like a dumb calculator.
But, believe it or not, it’s a giant step toward building an actual standalone compiler.</p>

<p>Theory aside, let’s dive into code.
We need two components: a lexer that also recognizes our one-rule grammar and an evaluator.</p>

<h2 id="lexer">Lexer</h2>

<p>The example code is written in Go.
We need a piece of logic that takes a string and splits it into three tokens:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="k">const</span> <span class="n">numberPattern</span> <span class="o">=</span> <span class="s">`\s*([+-]?\d*\.?\d+)\s*`</span>

<span class="k">var</span> <span class="n">exprRegex</span> <span class="o">=</span> <span class="n">regexp</span><span class="o">.</span><span class="n">MustCompile</span><span class="p">(</span><span class="s">`^`</span> <span class="o">+</span> <span class="n">numberPattern</span> <span class="o">+</span> <span class="s">`([+])`</span> <span class="o">+</span> <span class="n">numberPattern</span> <span class="o">+</span> <span class="s">`$`</span><span class="p">)</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>We start by defining a number and an expression using regular expressions.
Like it or not, regular languages are at the heart of many lexers, and regexes are a convenient way to describe them.
They let us group sequences of characters into logical units (<em>tokens</em>).
In our case, an expression always consists of three tokens: the first number, <code class="language-plaintext highlighter-rouge">+</code>, and the second number.</p>

<p>For this tiny language, one regex both validates the expression’s structure and tokenizes the input:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
</pre></td><td class="rouge-code"><pre><span class="k">func</span> <span class="n">interpret</span><span class="p">(</span><span class="n">line</span> <span class="kt">string</span><span class="p">)</span> <span class="p">(</span><span class="kt">float64</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">matches</span> <span class="o">:=</span> <span class="n">exprRegex</span><span class="o">.</span><span class="n">FindStringSubmatch</span><span class="p">(</span><span class="n">line</span><span class="p">)</span>
	<span class="k">if</span> <span class="n">matches</span> <span class="o">==</span> <span class="no">nil</span> <span class="p">{</span>
		<span class="k">if</span> <span class="n">strings</span><span class="o">.</span><span class="n">TrimSpace</span><span class="p">(</span><span class="n">line</span><span class="p">)</span> <span class="o">==</span> <span class="s">""</span> <span class="p">{</span>
			<span class="k">return</span> <span class="m">0</span><span class="p">,</span> <span class="n">errors</span><span class="o">.</span><span class="n">New</span><span class="p">(</span><span class="s">"error: empty expression"</span><span class="p">)</span>
		<span class="p">}</span>
		<span class="k">return</span> <span class="m">0</span><span class="p">,</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Errorf</span><span class="p">(</span><span class="s">"error: expected </span><span class="se">\"</span><span class="s">number + number</span><span class="se">\"</span><span class="s">, got %q"</span><span class="p">,</span> <span class="n">line</span><span class="p">)</span>
	<span class="p">}</span>

	<span class="n">left</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">right</span> <span class="o">:=</span> <span class="n">matches</span><span class="p">[</span><span class="m">1</span><span class="p">],</span> <span class="n">matches</span><span class="p">[</span><span class="m">2</span><span class="p">],</span> <span class="n">matches</span><span class="p">[</span><span class="m">3</span><span class="p">]</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>As you can see, we successfully extracted logical tokens from a simple string.
The actual evaluation is unsurprisingly straightforward:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
</pre></td><td class="rouge-code"><pre><span class="n">a</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">strconv</span><span class="o">.</span><span class="n">ParseFloat</span><span class="p">(</span><span class="n">left</span><span class="p">,</span> <span class="m">64</span><span class="p">)</span>
<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
	<span class="k">return</span> <span class="m">0</span><span class="p">,</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Errorf</span><span class="p">(</span><span class="s">"error: invalid number %q"</span><span class="p">,</span> <span class="n">left</span><span class="p">)</span>
<span class="p">}</span>
<span class="n">b</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">strconv</span><span class="o">.</span><span class="n">ParseFloat</span><span class="p">(</span><span class="n">right</span><span class="p">,</span> <span class="m">64</span><span class="p">)</span>
<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
	<span class="k">return</span> <span class="m">0</span><span class="p">,</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Errorf</span><span class="p">(</span><span class="s">"error: invalid number %q"</span><span class="p">,</span> <span class="n">right</span><span class="p">)</span>
<span class="p">}</span>

<span class="k">return</span> <span class="n">a</span> <span class="o">+</span> <span class="n">b</span><span class="p">,</span> <span class="no">nil</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>For the time being, the only thing our <em>interpreter</em> can do is add two numbers:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre><span class="nb">echo</span> <span class="s1">'2 + 1'</span> | go run main.go
3
</pre></td></tr></tbody></table></code></pre></div></div>

<p>It works!
I omitted some plumbing related to reading from <code class="language-plaintext highlighter-rouge">stdin</code>; you can find it on <a href="https://github.com/nurkiewicz/writing-compiler/tree/part-i">GitHub</a> (<code class="language-plaintext highlighter-rouge">part-i</code> branch).
In the next episode, we’ll handle all arithmetic operations, not just addition.
If you are a bit disappointed, bear with me.
We will soon learn how to skip the interpretation step and generate runnable artifacts (executables).</p>

<h2 id="write-yourself-a-compiler-series">Write yourself a compiler series</h2>

<ol>
  
    
    <li>
      
        <strong><a href="/2026/07/simplest-interpreter-write-yourself-a-compiler-part-i.html">The simplest interpreter</a></strong>
      
    </li>
  
    
    <li>
      
        <a href="/2026/07/arithmetic-interpreter-write-yourself-a-compiler.html">Arithmetic interpreter</a>
      
    </li>
  
    
    <li>
      
        <a href="/2026/08/compiling-to-intermediate-representation-write-yourself-a-compiler.html">Compiling to intermediate representation</a>
      
    </li>
  
    
    <li>
      
        <a href="/2026/08/your-first-virtual-machine-write-yourself-a-compiler.html">Your First Virtual Machine</a>
      
    </li>
  
    
    <li>
      
        <a href="/2026/09/generating-java-bytecode-write-yourself-a-compiler.html">Generating Java bytecode</a>
      
    </li>
  
    
    <li>
      
        <a href="/2026/09/interoperability-with-java-write-yourself-a-compiler.html">Interoperability with Java</a>
      
    </li>
  
</ol>]]></content><author><name></name></author><category term="writing-compiler" /><category term="compiler" /><category term="interpreter" /><category term="lexer" /><category term="go" /><summary type="html"><![CDATA[I’ve always felt that writing a compiler is the most romantic software engineering task. You’re writing a program that reads textual instructions describing precisely what a computer should do. Do not confuse this with prompting an LLM, where you write vague, verbose instructions that only loosely describe what a computer might do. But I digress.]]></summary></entry><entry><title type="html">Rewriting my 2005 C++/OpenGL game in Go</title><link href="https://nurkiewicz.com/2026/06/rewriting-cpp-opengl-game-in-go.html" rel="alternate" type="text/html" title="Rewriting my 2005 C++/OpenGL game in Go" /><published>2026-06-07T00:00:00+02:00</published><updated>2026-06-07T00:00:00+02:00</updated><id>https://nurkiewicz.com/2026/06/rewriting-cpp-opengl-game-in-go</id><content type="html" xml:base="https://nurkiewicz.com/2026/06/rewriting-cpp-opengl-game-in-go.html"><![CDATA[<p>Twenty years ago I wrote <a href="https://github.com/nurkiewicz/cute">CuTe</a> - a 3D Tetris clone in C++.
Raw OpenGL, WinAPI, <a href="https://www.boost.org/">Boost</a>, a hand-rolled XML parser.
It compiled with Visual Studio 2005 on Windows XP.
Not Visual Studio Code, which was released a decade later.
Visual Studio.
I only had a dial-up modem connection, so I had to download the majority of documentation and tutorials.
Including an official CD with Microsoft’s documentation.
I haven’t touched the source code since.</p>

<p>BTW Did you know the original author of VS Code is Erich Gamma, one of the authors of “<em>Design Patterns: Elements of Reusable Object-Oriented Software</em>”?</p>

<p>Anyway, I decided to see what happens when you point Claude Code at this codebase and say: <em>“rewrite this in Go, replace OpenGL with something portable, keep going until it runs.”</em>
Spoiler: it ran on the first build.
But “runs” and “feels right” turned out to be very different things.</p>

<h2 id="the-original-codebase">The original codebase</h2>

<p><img src="/assets/img/cute/screenshot2.png" alt="CuTe Go port running in demo mode" /></p>

<p>CuTe (Cubic Tetris) was a proper game.
Blocks are 3D shapes that fall into a cuboid.
You can move them in 3 dimensions (left-right, up-down, and shifting down) and rotate them around 3 axes.
When a bottom Z-plane fills up, it’s removed.
There’s an intro animation, a main menu, difficulty settings, high scores, customizable controls, a demo mode where the computer plays itself, sound effects, textures.
I believe it was even included on a CD attached to some Polish computer magazine.
That was a big thing back then.</p>

<p>About 3,000 lines of C++ across, heavy use of <a href="https://www.boost.org/releases/1.33.0/">boost 1.33</a>, a custom OpenGL code, a custom XML parser, and Win32 API for window management, keyboard.
The architecture is well-layered, which turned out to be the key insight for the rewrite:</p>

<ul>
  <li><strong><code class="language-plaintext highlighter-rouge">Engine</code></strong> - pure game logic: cuboid data, blocks, collision, scoring</li>
  <li><strong><code class="language-plaintext highlighter-rouge">EngineExt</code></strong> - smooth animation: position/angle interpolation, timing</li>
  <li><strong><code class="language-plaintext highlighter-rouge">GLEngine</code></strong> - OpenGL rendering: textured cubes, camera, walls, display lists</li>
  <li><strong><code class="language-plaintext highlighter-rouge">Game</code></strong> - scene composition, input handling, camera modes</li>
</ul>

<h2 id="choosing-a-graphics-library">Choosing a graphics library</h2>

<p>The original used the OpenGL, which is deprecated on MacOS and apparently no longer maintained (with version 4.6 release back in <a href="https://en.wikipedia.org/wiki/OpenGL">2017</a>).
I needed something portable.
<a href="https://github.com/gen2brain/raylib-go"><code class="language-plaintext highlighter-rouge">raylib-go</code></a> maps almost 1:1 to the original’s rendering calls:</p>

<p>Was:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre><span class="n">glColorHSV</span><span class="p">(</span><span class="n">z</span> <span class="o">*</span> <span class="n">M_PI</span> <span class="o">/</span> <span class="m">3</span><span class="p">,</span> <span class="m">1.0</span><span class="p">,</span> <span class="m">1.0</span><span class="p">);</span>
<span class="n">drawCube</span><span class="p">(</span><span class="n">x</span> <span class="o">+</span> <span class="m">0.5</span><span class="p">,</span> <span class="n">y</span> <span class="o">+</span> <span class="m">0.5</span><span class="p">,</span> <span class="n">z</span> <span class="o">+</span> <span class="m">0.5</span><span class="p">,</span> <span class="n">border</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Became:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre><span class="n">color</span> <span class="o">:=</span> <span class="n">rl</span><span class="o">.</span><span class="n">ColorFromHSV</span><span class="p">(</span><span class="kt">float32</span><span class="p">(</span><span class="n">z</span><span class="p">)</span><span class="o">*</span><span class="m">60.0</span><span class="p">,</span> <span class="m">1.0</span><span class="p">,</span> <span class="m">1.0</span><span class="p">)</span>
<span class="n">rl</span><span class="o">.</span><span class="n">DrawCube</span><span class="p">(</span><span class="n">pos</span><span class="p">,</span> <span class="n">size</span><span class="p">,</span> <span class="n">size</span><span class="p">,</span> <span class="n">size</span><span class="p">,</span> <span class="n">color</span><span class="p">)</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h2 id="the-first-version-750-lines-built-on-first-try">The first version: 750 lines, built on first try</h2>

<p>The first version <em>spit</em> from Claude Code compiled, a window appeared, blocks fell in 3D, planes were removed.
That was pretty astonishing.
I was particularly impressed that Claude was taking screenshots of the running desktop app to make adjustments on the fly.
This allow it to make feedback loops all by itself.
However, a ton of functionality was missing, and some features were too obscure for Claude.
So it just straight up ignored them.</p>

<div style="text-align: center;">
<iframe width="560" height="315" src="https://www.youtube.com/embed/JvCp5HvKt5c?si=0_eTSEirhFF8ZMpY" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen=""></iframe>
</div>

<h2 id="the-artificial-intelligence-that-plays-itself">The artificial intelligence that plays itself</h2>

<p>My original game had a demo mode, which essentially plays itself.
The algorithm to play 3D tetris was a straightforward brute-force.
For each block, it tests all 24 unique orientations (there are exactly 24 distinct rotations of a cube).
For each orientation, it tries every (x, y) position on the board.
For each valid position, it computes a fit factor for every cube:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>heights * (-8) + holes_below * (-256) + touching_neighbors * 1
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Deeper placement is better (negative height weight).
Holes below the block (obscuring a gap in lower plane) are terrible because they prevent lower planes from getting filled.
Touching existing cubes side-by-side is good (better fit).</p>

<p>On my original machine back then the entire brute-force was taking way too long.
Algorithm needed time-slicing for this: code would check a timer and yield after 15ms to prevent frame drops.
I had to invent cooperative multi-tasking - if brute-force algorithm takes too much time, pause, let the frame render, resume.
I must’ve felt really smart back then.
If I had know known C++ and boost library had proper multi-threading back then…</p>

<p>Anyway, on a 2005 Pentium, analyzing 24 rotations x 36 positions (with my poor coding skills) apparently took noticeable time.
If I simply wanted to analyze all options, my naive algorithm caused noticeable hickup in the animation.
Thus, I had to invent cooperative multi-tasking.
On an M1 Pro, it takes microseconds.
Code just runs the whole analysis in one synchronous call.
No goroutines, no channels, no time-slicing.</p>

<p>Fun fact: this is how I represented rotations of a cube in 3D space.
I believe lower-case was clockwise, capital-case: counter-clockwise.
Of course, the code is already translated to Go:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="rouge-code"><pre><span class="k">var</span> <span class="n">rotCodes</span> <span class="o">=</span> <span class="p">[</span><span class="m">24</span><span class="p">]</span><span class="kt">string</span><span class="p">{</span>
    <span class="s">""</span><span class="p">,</span> <span class="s">"x"</span><span class="p">,</span> <span class="s">"XX"</span><span class="p">,</span> <span class="s">"X"</span><span class="p">,</span> <span class="s">"yZ"</span><span class="p">,</span> <span class="s">"y"</span><span class="p">,</span> <span class="s">"yz"</span><span class="p">,</span> <span class="s">"zzY"</span><span class="p">,</span> <span class="s">"zz"</span><span class="p">,</span>
    <span class="s">"YYX"</span><span class="p">,</span> <span class="s">"YY"</span><span class="p">,</span> <span class="s">"YYx"</span><span class="p">,</span> <span class="s">"Zy"</span><span class="p">,</span> <span class="s">"ZYY"</span><span class="p">,</span> <span class="s">"ZY"</span><span class="p">,</span> <span class="s">"Z"</span><span class="p">,</span> <span class="s">"YZ"</span><span class="p">,</span>
    <span class="s">"Y"</span><span class="p">,</span> <span class="s">"Yz"</span><span class="p">,</span> <span class="s">"Yzz"</span><span class="p">,</span> <span class="s">"zy"</span><span class="p">,</span> <span class="s">"zyy"</span><span class="p">,</span> <span class="s">"zY"</span><span class="p">,</span> <span class="s">"z"</span><span class="p">,</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Any orientation is reachable in at most 3 rotations.</p>

<p><img src="/assets/img/cute/screenshot.png" alt="CuTe Go port — demo mode with a filled board" /></p>

<h2 id="things-claude-code-decided-to-drop-just-because">Things Claude Code decided to drop “just because”</h2>

<p>Even though I asked to port the entire codebase into Go, Claude Code simply assumed some features are just not worth it.
I felt a bit offended.
Here’s the final output, I appreciate the passive-aggressive comment about my XOR <em>encryption</em>:</p>

<blockquote>
  <ul>
    <li>Intro animation (XML-driven OpenGL command sequences - cool but not worth porting)</li>
    <li>Sound effects (the <code class="language-plaintext highlighter-rouge">.dat</code> files are in a custom format)</li>
    <li>High score persistence (XOR-“encrypted” XML files - charmingly 2005)</li>
    <li>Custom key bindings (the options menu was a beast of nested classes)</li>
    <li>Multi-language support (Polish and English via XML language packs)</li>
  </ul>
</blockquote>

<p>Although I do understand some design choices were questionable (XOR encryption, custom asset format, custom XML parser) - I don’t think it justifies Claude to simply drop them.
Just do what I pay you for!</p>

<h2 id="what-i-learned">What I learned</h2>

<p>Clean separation of game logic, animation, and rendering meant Claude could port each layer independently.
The game engine is basically the same code in Go syntax.
The rendering is a thin translation layer.</p>

<p>The hard parts weren’t the obvious ones (OpenGL calls, WinAPI, Boost).
They were the feel details: animation rate-limiting, camera defaults, color interpolation, fade-out timing.
These are the things that are invisible when reading code but immediately wrong when playing the game.</p>

<p>The iterative approach worked better than trying to get everything right upfront.
Each round of <em>“this doesn’t feel right”</em> led to investigating a specific aspect of the original’s behavior.
The C++ comments were occasionally helpful, but watching the game run (or fail to run correctly) was the real specification.</p>

<h2 id="source-code">Source code</h2>

<ul>
  <li><a href="https://github.com/nurkiewicz/cute">the original source code in C++</a> - maybe there’s someone willing to run the original codebase on modern machine?</li>
  <li><a href="https://github.com/nurkiewicz/CuTe/tree/go-port">ported source code</a></li>
</ul>]]></content><author><name></name></author><category term="go" /><category term="raylib" /><category term="opengl" /><category term="gamedev" /><category term="ai" /><summary type="html"><![CDATA[I asked Claude Code to rewrite CuTe, my 20-year-old 3D Tetris clone, from C++/OpenGL/WinAPI into modern Go. What went well, what broke, and what I had to fix four times.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://nurkiewicz.com/assets/img/cute/screenshot-hero.png" /><media:content medium="image" url="https://nurkiewicz.com/assets/img/cute/screenshot-hero.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">OpenTelemetry glossary: 30 terms you should know</title><link href="https://nurkiewicz.com/2026/04/open-telemetry-glossary.html" rel="alternate" type="text/html" title="OpenTelemetry glossary: 30 terms you should know" /><published>2026-04-15T00:00:00+02:00</published><updated>2026-04-15T00:00:00+02:00</updated><id>https://nurkiewicz.com/2026/04/open-telemetry-glossary</id><content type="html" xml:base="https://nurkiewicz.com/2026/04/open-telemetry-glossary.html"><![CDATA[<p>If you’ve ever stared at a Grafana dashboard pretending to understand what’s going on - this post is for you.
Observability is one of those areas where everyone nods along in meetings but secretly Googles half the terms afterwards.
I’ve been that person.
More than once.
The following is a glossary of the most important observability and OpenTelemetry concepts.</p>

<dl>
  <dt>Observability</dt>
  <dd>The ability to understand what’s happening inside your system by examining its external outputs.
Unlike traditional monitoring, where you define upfront what to watch, observability lets you ask arbitrary questions about your system’s behavior <em>after the fact</em>.</dd>
  <dt>Trace</dt>
  <dd>The complete journey of a single request as it travels through your distributed system.
It starts when a user clicks a button and ends when the response comes back, capturing every service, database call, and queue hop along the way.
Without traces, debugging a slow request in a microservices architecture is like finding a needle in a haystack.
I covered this topic in detail in my podcast about <a href="https://nurkiewicz.com/48">distributed tracing</a>.</dd>
  <dt>Span</dt>
  <dd>A single unit of work within a trace.
Like one leg of a relay race.
Each span records a specific operation: an HTTP call, a database query, or a message being published.
Spans are nested - a parent span for handling an HTTP request may contain child spans for authentication, business logic, and persistence.
What you see on the header picture is a single trace with hundreds of spans.
Each horizontal bar is a span.</dd>
  <dt>Context propagation</dt>
  <dd>The mechanism that links related operations across service boundaries.
When service A calls service B, context propagation injects trace identifiers into the request (usually as HTTP headers like W3C <code class="language-plaintext highlighter-rouge">traceparent</code>).
Service B extracts them and continues the same trace.
Without this, you’d have isolated spans with no way to stitch them into a complete picture.
It’s the glue that makes distributed tracing <em>distributed</em>.</dd>
  <dt>Metric</dt>
  <dd>A numeric measurement collected over time: request count, error rate, CPU usage, queue depth.
Metrics are cheap to store and query, making them perfect for dashboards and alerting.
They tell you <em>that</em> something is wrong, but rarely <em>why</em>.
That’s where traces and logs come in.</dd>
  <dt>Log</dt>
  <dd>A discrete event recorded by your application at a specific point in time.
The oldest and most familiar form of observability data.
Every developer has written a <code class="language-plaintext highlighter-rouge">log.info()</code> at some point.
The challenge isn’t producing logs - it’s making them searchable and correlated with traces and metrics.
Logging at scale is serious business.</dd>
  <dt>Structured logging</dt>
  <dd>Logging where each entry is a set of key-value pairs rather than a free-form string.
Instead of <code class="language-plaintext highlighter-rouge">"User 42 logged in from 192.168.1.1"</code>, you emit <code class="language-plaintext highlighter-rouge">{event: "login", userId: 42, ip: "192.168.1.1"}</code>.
This makes logs machine-parseable, filterable, and actually useful at scale.
<code class="language-plaintext highlighter-rouge">grep</code> doesn’t cut it when you process terabytes of logs daily.</dd>
  <dt>Backend</dt>
  <dd>The system that receives, stores, and lets you query your telemetry data.
Jaeger, Prometheus, Datadog, Grafana Tempo - these are all backends.
Your application produces traces, metrics, and logs.
The backend is where they end up and where you actually go to make sense of them.</dd>
  <dt>OpenTelemetry (OTel)</dt>
  <dd>A vendor-neutral, open-source observability framework for generating, collecting, and exporting telemetry data.
It provides APIs, SDKs, and tools for all three pillars: traces, metrics, and logs.
Instrument your code once, send data to any backend you choose.
If you learn only one thing from this glossary, let it be OTel.</dd>
  <dt>OTLP (OpenTelemetry Protocol)</dt>
  <dd>The native wire protocol for transmitting telemetry data in OpenTelemetry.
OTLP defines how traces, metrics, and logs are encoded and transported - typically over gRPC or HTTP.
Most backends accept OTLP directly nowadays, which means you can often skip format-specific exporters entirely.
One protocol to rule them all.</dd>
  <dt>OpenTelemetry instrumentation</dt>
  <dd>The process of adding observability to your application using OTel’s APIs and SDKs.
Auto-instrumentation can inject tracing into popular frameworks (Spring Boot, Express, Django) with zero code changes.
Feels like magic, and mostly works.
Manual instrumentation gives you finer control - adding custom spans, attributes, or metrics where the automatic approach falls short.</dd>
  <dt>OpenTelemetry Collector</dt>
  <dd>A vendor-agnostic proxy that receives, processes, and exports telemetry data.
Instead of configuring each application to send data directly to your backend, you route everything through the Collector.
It decouples your instrumentation from your backend choice and lets you transform, filter, or enrich data in transit.
Switching from Jaeger to Tempo?
Change the Collector config.
Your applications don’t need to know.</dd>
  <dt>Receiver</dt>
  <dd>The Collector component that ingests telemetry data from your applications.
Receivers support various protocols and formats: OTLP, Jaeger, Zipkin, Prometheus, and many more.
You can run multiple receivers simultaneously, which is invaluable when migrating from one instrumentation library to another.
Migration without downtime.</dd>
  <dt>Processor</dt>
  <dd>The middle layer of the Collector pipeline that transforms data between receiving and exporting.
Processors can batch data for efficiency, filter out noisy spans, sample only a percentage of traces, or enrich telemetry with additional attributes.
Order matters.
Processors execute sequentially, and a poorly placed filter can drop data before it gets enriched.</dd>
  <dt>Exporter</dt>
  <dd>The Collector component that sends processed telemetry to your backend of choice.
Exporters exist for virtually every observability platform: Jaeger, Prometheus, Datadog, Splunk, you name it.
You can even configure multiple exporters simultaneously - send traces to Jaeger for debugging and to long-term storage for compliance.
Why choose?</dd>
  <dt>Connector</dt>
  <dd>A relatively new Collector component that acts as both an exporter and a receiver, bridging two pipelines.
A connector can analyze incoming spans and generate metrics from them - counting errors or measuring latency without separate instrumentation.
Deriving one signal from another.</dd>
  <dt>APM (Application Performance Monitoring)</dt>
  <dd>A broad category of tools that monitor and manage the performance and availability of your applications.
APM solutions typically combine traces, metrics, and logs into a unified experience with dashboards, alerting, and root cause analysis.
Every vendor defines APM slightly differently, but the goal is always the same: know when your app is slow or broken, and understand <em>why</em>.</dd>
  <dt>Prometheus</dt>
  <dd>The de facto standard for metrics collection in the cloud-native world.
Prometheus uses a pull model - it <em>scrapes</em> metrics from your applications at regular intervals, rather than waiting for them to be pushed.
Its query language, PromQL, is powerful but has a learning curve that makes regex look approachable.</dd>
  <dt>Grafana</dt>
  <dd>A visualization and dashboarding platform that can display data from virtually any source.
Grafana doesn’t store data itself - it connects to Prometheus, Loki, Tempo, and dozens of other backends.
If Prometheus is the engine, Grafana is the dashboard on your car.
Pretty, informative, and the part everyone actually looks at.</dd>
</dl>

<p><img src="/assets/img/telemetry-glossary/grafana-dashboard.png" alt="Grafana dashboard based on Node Exporter Full pre-made template" />
<em>Dashboard based on <a href="https://grafana.com/grafana/dashboards/1860-node-exporter-full/">Node Exporter Full</a> pre-made template</em></p>

<dl>
  <dt>Loki</dt>
  <dd>A log aggregation system, inspired by Prometheus.
Unlike Elasticsearch, Loki indexes only metadata (labels) rather than the full text of log lines.
Much cheaper to operate.
The trade-off?
Full-text search is slower.
But in practice, filtering by labels and then scanning is fast enough for most use cases.</dd>
  <dt>Tempo</dt>
  <dd>A distributed tracing backend, designed for massive scale at minimal cost.
Tempo stores traces in object storage (like S3) without requiring any indexing infrastructure.
The catch: you need a trace ID to look up a trace.
But integrations with Loki and Grafana make discovery seamless, so it’s less of a problem than it sounds.</dd>
  <dt>Jaeger</dt>
  <dd>An open-source distributed tracing platform, originally built by Uber.
Jaeger helps you visualize request flows and latency bottlenecks in complex distributed systems.</dd>
  <dt>Zipkin</dt>
  <dd>One of the earliest open-source distributed tracing systems, inspired by <a href="https://research.google/pubs/dapper-a-large-scale-distributed-systems-tracing-infrastructure/">Google’s Dapper paper</a>.
Zipkin pioneered many concepts that are now standard, including the B3 propagation format - a set of HTTP headers (<code class="language-plaintext highlighter-rouge">X-B3-TraceId</code>, <code class="language-plaintext highlighter-rouge">X-B3-SpanId</code>, etc.) that carry trace context between services.
Still actively maintained and has a loyal following, though newer tools have captured more mindshare.</dd>
  <dt>Mimir</dt>
  <dd>A highly scalable, long-term storage backend for Prometheus metrics.
Prometheus is great for short-term storage, but Mimir can handle months or years of metrics data across multiple tenants.</dd>
  <dt>Thanos</dt>
  <dd>Another approach to scaling Prometheus, focused on long-term storage and global querying across multiple Prometheus instances.
Thanos sits <em>alongside</em> your existing Prometheus servers and uploads their data to object storage, providing a unified query layer.
If Mimir <em>replaces</em> Prometheus, Thanos <em>extends</em> it.</dd>
  <dt>Alloy</dt>
  <dd>Open-source telemetry collector, previously known as Grafana Agent.
Collects metrics, logs, traces, and profiles, then ships them to your backends.</dd>
  <dt>Pyroscope</dt>
  <dd>A continuous profiling platform.
While traces tell you <em>where</em> time is spent across services, Pyroscope tells you <em>where</em> time is spent <em>within a single process</em> - down to the function and line of code.
Some call profiling the “fourth pillar” of observability.
I’d say the jury is still out, but Pyroscope makes it accessible in production without significant overhead.</dd>
</dl>

<p><img src="/assets/img/telemetry-glossary/pyroscope-dashboard.png" alt="Pyroscope dashboard in Grafana" />
<em>Source: <a href="https://grafana.com/docs/grafana/latest/datasources/pyroscope/">Grafana Pyroscope documentation</a></em></p>

<dl>
  <dt>Commercial APM platforms (<a href="https://www.datadoghq.com/">Datadog</a>, <a href="https://www.splunk.com/">Splunk</a>, <a href="https://newrelic.com/">New Relic</a>)</dt>
  <dd>Full-stack observability solutions that bundle metrics, traces, logs, dashboards, and alerting into a single managed product.
They all solve the same problem: you don’t want to run your own observability infrastructure.
The trade-off is cost and vendor lock-in.
OpenTelemetry helps with the lock-in part - instrument once, switch backends later.</dd>
  <dt>ELK Stack (Elasticsearch, Logstash, Kibana)</dt>
  <dd>The classic open-source trio for log management.
Elasticsearch stores and indexes, Logstash ingests and transforms, Kibana visualizes.
For years, ELK was <em>the</em> answer to “where do I search my logs?”
Powerful, but running Elasticsearch at scale is practically a full-time job.
The Grafana stack (Loki + Grafana) is a lighter alternative - Loki skips full-text indexing, which trades search flexibility for much lower operational cost.
ELK predates OpenTelemetry and focuses primarily on logs, while OTel covers all three signals and is backend-agnostic.
That said, you can absolutely feed OTel data into Elasticsearch.</dd>
  <dt>Kibana</dt>
  <dd>The visualization layer of the Elastic Stack (formerly ELK: Elasticsearch, Logstash, Kibana).
Provides dashboards and exploration tools for data stored in Elasticsearch, particularly logs.
While Grafana has become the default for metrics, Kibana remains the go-to for teams heavily invested in Elasticsearch.</dd>
</dl>

<hr />

<h2 id="further-reading">Further reading</h2>

<ul>
  <li><a href="https://opentelemetry.io/blog/2026/demystifying-opentelemetry/">Demystifying OpenTelemetry</a></li>
</ul>]]></content><author><name></name></author><category term="observability" /><category term="opentelemetry" /><category term="monitoring" /><category term="grafana" /><category term="prometheus" /><summary type="html"><![CDATA[A glossary of OpenTelemetry and observability terms, from traces and spans to Grafana and Datadog. Opinionated definitions for developers who don't have time for dry documentation.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://nurkiewicz.com/assets/img/telemetry-glossary/opentelemetry-glossary.png" /><media:content medium="image" url="https://nurkiewicz.com/assets/img/telemetry-glossary/opentelemetry-glossary.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>