<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Lahari Tenneti</title>
    <description>The latest articles on DEV Community by Lahari Tenneti (@lahari_tenneti_4a8a082e9c).</description>
    <link>https://dev.to/lahari_tenneti_4a8a082e9c</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3479289%2F25c84943-6853-40b8-83d9-4333147f4b0d.png</url>
      <title>DEV Community: Lahari Tenneti</title>
      <link>https://dev.to/lahari_tenneti_4a8a082e9c</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lahari_tenneti_4a8a082e9c"/>
    <language>en</language>
    <item>
      <title>LLVM #2 — Optimiser Support &amp; JIT Compilation</title>
      <dc:creator>Lahari Tenneti</dc:creator>
      <pubDate>Tue, 17 Mar 2026 06:48:33 +0000</pubDate>
      <link>https://dev.to/lahari_tenneti_4a8a082e9c/llvm-2-optimiser-support-jit-compilation-15hm</link>
      <guid>https://dev.to/lahari_tenneti_4a8a082e9c/llvm-2-optimiser-support-jit-compilation-15hm</guid>
      <description>&lt;p&gt;Having understood what compiler optimisations are and how they work in theory, we now actually wire them into Kaleidoscope, and then take things one step further by adding a JIT compiler so our REPL can evaluate expressions on the spot.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I built:&lt;/strong&gt; &lt;a href="https://github.com/laharitenneti/Kaleidoscope/commit/91267d2382ab9ff11d8a907290aca1ae0168b81f" rel="noopener noreferrer"&gt;Commit 91267d2&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What I understood:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1) Adding Optimisation Passes:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;So far, our codegen was correct but not efficient. The IR we produced was merely a pretty-print of the AST. LLVM provides a &lt;code&gt;FunctionPassManager&lt;/code&gt; to change that.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A pass is simply one "go" over the IR that looks for a specific pattern and rewrites it. The &lt;code&gt;FunctionPassManager&lt;/code&gt; contains a sequence of passes and runs them over each function in that order, passing the output of one as input to the next.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The key change is in &lt;code&gt;InitialiseModuleAndManagers()&lt;/code&gt;. After creating the module and the IR builder, we now also have a whole suite of analysis and pass managers:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;TheFPM&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;make_unique&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;FunctionPassManager&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;TheLAM&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;make_unique&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;LoopAnalysisManager&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;TheFAM&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;make_unique&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;FunctionAnalysisManager&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;TheCGAM&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;make_unique&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CGSCCAnalysisManager&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;TheMAM&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;make_unique&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ModuleAnalysisManager&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The four &lt;code&gt;AnalysisManagers&lt;/code&gt; each correspond to a level of LLVM's IR hierarchy — loops, functions, call-graph SCCs, and whole modules.&lt;/li&gt;
&lt;li&gt;They're required so transform passes can look up analysis results when they need them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Side note:&lt;/strong&gt; A call graph is one with functions for nodes, with every edge representing a call. So an edge from A to B means A calls B.&lt;/li&gt;
&lt;li&gt;SCCs (Strongly Connected Components) in a call graph represent a group of functions where each function can reach/call the other. (Mutual recursion of sorts)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then we create four transform passes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;TheFPM&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;addPass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;InstCombinePass&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;    &lt;span class="c1"&gt;// peephole optimisations&lt;/span&gt;
&lt;span class="n"&gt;TheFPM&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;addPass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ReassociatePass&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;    &lt;span class="c1"&gt;// reorder expressions&lt;/span&gt;
&lt;span class="n"&gt;TheFPM&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;addPass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GVNPass&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;            &lt;span class="c1"&gt;// eliminate redundant computations&lt;/span&gt;
&lt;span class="n"&gt;TheFPM&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;addPass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SimplifyCFGPass&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;    &lt;span class="c1"&gt;// clean up unreachable blocks&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;A) InstCombinePass:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Handles "peephole" optimisations (small/local rewrites).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;1+2&lt;/code&gt; becoming &lt;code&gt;3.0&lt;/code&gt; before the program ever runs is constant folding at play, and this pass is what catches it in the generated IR.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;B) ReassociatePass:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reorders expressions to enable more opportunities for other passes.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;(x+1)+2&lt;/code&gt; becomes &lt;code&gt;x+3&lt;/code&gt;. Small change, but it means GVN can now recognise that &lt;code&gt;(x+1)+2&lt;/code&gt; and &lt;code&gt;1+(x+2)&lt;/code&gt; are the same thing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;C) GVNPass (Global Value Numbering):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Assigns a symbolic identity to each new/unique computation and replaces duplicates.&lt;/li&gt;
&lt;li&gt;Ex: if we write &lt;code&gt;(1+2+x)*(x+(1+2))&lt;/code&gt;, both sides of the multiplication are &lt;code&gt;x+3&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Without GVN, the IR computes &lt;code&gt;x+3&lt;/code&gt; twice. With it, the result is computed once and reused.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So before GVN:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight llvm"&gt;&lt;code&gt;&lt;span class="nv"&gt;%addtmp&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;fadd&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="m"&gt;3.000000e+00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;%x&lt;/span&gt;
&lt;span class="nv"&gt;%addtmp1&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;fadd&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="nv"&gt;%x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;3.000000e+00&lt;/span&gt;
&lt;span class="nv"&gt;%multmp&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;fmul&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="nv"&gt;%addtmp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;%addtmp1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After GVN:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight llvm"&gt;&lt;code&gt;&lt;span class="nv"&gt;%addtmp&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;fadd&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="nv"&gt;%x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;3.000000e+00&lt;/span&gt;
&lt;span class="nv"&gt;%multmp&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;fmul&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="nv"&gt;%addtmp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;%addtmp&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;D) SimplifyCFGPass:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cleans up the control flow graph. If a branch can never be entered into, or a block has no predecessors, this pass removes it.&lt;/li&gt;
&lt;li&gt;It's more like keeping the IR tidy after the other passes have finished their work.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally, we run the pass manager after every function is constructed, just before returning it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;TheFPM&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;TheFunction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;TheFAM&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;FunctionPassManager&lt;/code&gt; updates the function in-place. The IR going in is the naive transcription; the IR coming out is the cleaned-up, optimised version.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;2) JIT Compilation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Now that we have nice IR coming out of the optimiser, we want to execute it — not just pretty-print it. That's where the JIT comes in.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;JIT (Just-In-Time) compilation means converting LLVM IR to native machine code at runtime, in memory, right as the user types. The result is a pointer to executable code we can call directly, as if it were a C function.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Setting it up requires initialising the native target first:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;InitializeNativeTarget&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;InitializeNativeTargetAsmPrinter&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;InitializeNativeTargetAsmParser&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;TheJIT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ExitOnErr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;KaleidoscopeJIT&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="c1"&gt;// Side note: LLVM is cross-platform by design. These initialisation calls are what tell&lt;/span&gt;
&lt;span class="c1"&gt;// the JIT to look at the hardware the user is on and prepare to speak its specific language.&lt;/span&gt;
&lt;span class="c1"&gt;// Ex: Even if we're on Apple Silicon (arm64), LLVM could generate x86 code if we told it to.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And setting the module's data layout to match the JIT's:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;TheModule&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;setDataLayout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TheJIT&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;getDataLayout&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;This is important as it ensures that the memory layout of structs, function arguments, and return values matches what the host machine expects.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When the user types a top-level expression like &lt;code&gt;4+5;&lt;/code&gt;, we wrap it in an anonymous function (&lt;code&gt;__anon_expr&lt;/code&gt;), add the module to the JIT, look up the symbol, and call it:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;ExprSymbol&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ExitOnErr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TheJIT&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"__anon_expr"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;FP&lt;/span&gt;&lt;span class="p"&gt;)()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ExprSymbol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toPtr&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;fprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Evaluated to %f&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FP&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The JIT compiles the LLVM IR to machine code, returns its address, we cast it to a function pointer, and call it like any other native function.&lt;/li&gt;
&lt;li&gt;There's no difference at the hardware level between JIT-compiled code and statically linked machine code (i.e., they live in the same address space).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After calling it, we clean it up:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;ExitOnErr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RT&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;ResourceTracker&lt;/code&gt; (&lt;code&gt;RT&lt;/code&gt;) is responsible for the JIT'd memory allocated to that anonymous expression. Removing it frees that memory.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;3) The module lifetime problem:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The issue in the REPL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ready&amp;gt; def testfunc(x y) x + y*2;
ready&amp;gt; testfunc(4, 10);
Evaluated to 24.000000

ready&amp;gt; testfunc(5, 10);
LLVM ERROR: Program used external function 'testfunc' which could not be resolved!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;testfunc&lt;/code&gt; was defined in the same module as the anonymous expression for &lt;code&gt;testfunc(4, 10)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When we removed that module from the JIT to free the memory for the anonymous expression, we inadvertently deleted &lt;code&gt;testfunc&lt;/code&gt; along with it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The solution: Every function definition gets its own module. The JIT can resolve calls across module boundaries, so &lt;code&gt;testfunc&lt;/code&gt; lives in its own module indefinitely. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Each new anonymous expression gets a fresh module, which we remove after execution. Function definitions stay.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;But this creates a &lt;u&gt;new problem&lt;/u&gt;: when the codegen for a new anonymous expression tries to emit a call to &lt;code&gt;testfunc&lt;/code&gt;, that function doesn't exist in the current module. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The IR emitter needs at least a declaration of &lt;code&gt;testfunc&lt;/code&gt; to generate a valid &lt;code&gt;call&lt;/code&gt; instruction.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;u&gt;solution&lt;/u&gt; is &lt;code&gt;getFunction()&lt;/code&gt;, a helper that first checks the current module for a declaration, and if it doesn't find one, regenerates it from &lt;code&gt;FunctionProtos&lt;/code&gt; (a map of the most recent prototype for every function we've seen):&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;Function&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nf"&gt;getFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;F&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TheModule&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;getFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;FI&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FunctionProtos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FI&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;FunctionProtos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;FI&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;second&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;codegen&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;nullptr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;So the function "body" lives in its own module in the JIT.&lt;/li&gt;
&lt;li&gt;The function "declaration" gets re-emitted into each new module that needs to call it.&lt;/li&gt;
&lt;li&gt;The JIT links them at call time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fllyp4upwj4mphefnw20r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fllyp4upwj4mphefnw20r.png" alt=" " width="800" height="1062"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What's next:&lt;/strong&gt; Control flow (if/then/else and loops).&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Musings:&lt;/strong&gt;&lt;br&gt;
Learning the piano taught me something about passes. When you're learning a new piece, you don't play the whole thing perfectly on the first try. You play it slowly, fix the wrong notes, correct the timing, then fix the phrasing, then the dynamics. Each run is a pass, and each one builds on the last until what comes out sounds nothing like the stumbling first attempt, but means exactly the same thing. The optimiser does the same thing. The IR that enters is technically correct and the IR that exits is still correct; only better.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8kgwhyquel08cbbecl0l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8kgwhyquel08cbbecl0l.png" alt=" " width="800" height="1007"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>compilers</category>
      <category>llvm</category>
      <category>computerscience</category>
      <category>learning</category>
    </item>
    <item>
      <title>Compiler Optimisations</title>
      <dc:creator>Lahari Tenneti</dc:creator>
      <pubDate>Tue, 24 Feb 2026 11:03:40 +0000</pubDate>
      <link>https://dev.to/lahari_tenneti_4a8a082e9c/compiler-optimisations-6al</link>
      <guid>https://dev.to/lahari_tenneti_4a8a082e9c/compiler-optimisations-6al</guid>
      <description>&lt;p&gt;Before we perform compiler optimisations, we must know what they are, why they're needed, and how we do them.&lt;/p&gt;

&lt;p&gt;&lt;u&gt;What:&lt;/u&gt;&lt;br&gt;
Compiler Optimisations are systematic transformations that rewrite our source code into faster, smaller machine code without changing its meaning.&lt;/p&gt;

&lt;p&gt;&lt;u&gt;Why:&lt;/u&gt;&lt;br&gt;
They're needed because programs are full of inefficiencies—redundant calculations, impossible branches, or operations the CPU could do cheaper. Raw code from the parser is readable but slow, so optimisations squeeze out every drop of performance. &lt;/p&gt;

&lt;p&gt;Importantly, many impactful optimisations aren't fully hardware-agnostic. Universal ones like "this expression is always 0, delete it" work anywhere, but the big wins (like using SIMD registers, scheduling for a chip's pipeline, or picking specific CPU instructions) are often hardware-specific. &lt;/p&gt;

&lt;p&gt;Without a shared layer like LLVM's IR, every backend duplicates this effort. LLVM lets you write optimisations once against the IR, with backends handling hardware translation later.&lt;/p&gt;

&lt;p&gt;&lt;u&gt;How:&lt;/u&gt;&lt;br&gt;
Compilers apply optimisations in structured passes over the code. First local (within basic blocks, like folding constants), then global (across the function, like dead code elimination), often in multiple rounds. Each pass analyses data flow, rewrites IR, and repeats until no more gains are possible.&lt;/p&gt;

&lt;p&gt;As I'd mentioned in the previous post, LLVM uses SSA for this. In action, it's the &lt;code&gt;%addtmp&lt;/code&gt; or &lt;code&gt;%multmp1&lt;/code&gt; variables we keep seeing in Kaleidoscope's output. While it looks redundant (mostly because we're used to seeing the output directly), it's what optimisation ultimately depends on.&lt;/p&gt;

&lt;p&gt;In most programming languages, we can change/reassign a variable's value whenever we want.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;x = 5
x = x + 2
x = 10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In SSA form, every variable is assigned exactly once. If we wish to change the value of x, the compiler creates a new version of it. (Remember persistent data structures and the blockchain example from the Lox resolver?) Hence, the code above looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;%x1 = 5
%x2 = %x1 + 2
%x3 = 10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In fact, through dead-code elimination (see below), it would become more like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;%x3 = 10 #because %x2 isn't used anywhere and %x3 comes immediately after
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is to address the issue of Data Flow Analysis — where did a value come from?&lt;/p&gt;

&lt;p&gt;In non-SSA code, if you see a variable on a certain line, you'll have to look at every line before it to figure out which assignment currently "owns" that variable.&lt;/p&gt;

&lt;p&gt;But in SSA, the name of the variable is its definition. We know exactly where &lt;code&gt;%x2&lt;/code&gt; was born and what value it holds.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;How SSA is used in Compiler Optimisations:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1) &lt;u&gt;Constant Propagation and Folding:&lt;/u&gt;&lt;/p&gt;

&lt;p&gt;It took me a while to understand that these were two different things. But it's only that they both feed into each other.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Propagation&lt;/strong&gt; is simply fetching values. If I know the value of a constant used in certain expressions, I'll just replace that variable with its value directly.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;r = 5
area = 3.14 * r * r
#becomes
area = 3.14 * 5 * 5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Folding&lt;/strong&gt; is merely precomputing known values — the actual math is performed at compile-time instead of runtime. It's like saying, "I know the answer to this. Why do I make the CPU calculate it later?"
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;area = 3.14 * 5 * 5
#becomes
area = 78.5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2) &lt;u&gt;Value Range Propagation:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tracking the possible range of values a variable can hold, so the compiler can make smarter decisions later in the program.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (x &amp;gt; 0 &amp;amp;&amp;amp; x &amp;lt; 10):
    y = x * 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The compiler now knows &lt;code&gt;y&lt;/code&gt; must be in the range (0, 20). If a later condition asks something like &lt;code&gt;if (y &amp;gt; 100)&lt;/code&gt;, the compiler can eliminate that branch entirely because it's impossible.&lt;/li&gt;
&lt;li&gt;SSA makes it easy to track a variable's range through the program since each definition has a single, known origin.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;3) &lt;u&gt;Sparse Conditional Constant Propagation:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Constant propagation + branch awareness; only propagating values along branches that are actually reachable.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;x = 5
if (x &amp;gt; 10):
    y = x * 2   #dead; compiler knows x = 5 can never satisfy x &amp;gt; 10
else:
    y = x + 1   #compiler propagates: y = 6
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;SSA names are unique per definition, so once the compiler knows a branch is dead, every variable inside it is unreachable too. This avoids wastage of effort.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;4) &lt;u&gt;Dead Code Elimination:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Removing code that will never execute or whose result is never used.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;x = 10
y = x + 5    #y is never used again
return x
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;x = 10
return x
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Every SSA variable has a list of uses. If that list is empty, the variable (and the code that produced it) is eliminated.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;5) &lt;u&gt;Global Value Numbering:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Assigning a symbolic "number" to each unique computation, then replacing duplicates that produce the same result with a single reference.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;a = x + y
b = x + y
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Both expressions get the same value number. The compiler replaces &lt;code&gt;b&lt;/code&gt; with &lt;code&gt;a&lt;/code&gt;, computing &lt;code&gt;x + y&lt;/code&gt; only once, even if &lt;code&gt;a&lt;/code&gt; and &lt;code&gt;b&lt;/code&gt; are in different parts of the function. Again, SSA at play.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;6) &lt;u&gt;Partial Redundancy Elimination:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Removing calculations that are redundant on some paths through the program, by hoisting them to a point where they cover all paths.&lt;/li&gt;
&lt;li&gt;In the following example, in case of &lt;code&gt;heavy_traffic = true&lt;/code&gt;, The CPU calculates &lt;code&gt;distance/speed&lt;/code&gt; inside the &lt;code&gt;if&lt;/code&gt; block, then calculates it again at the end. That’s unnecessary.&lt;/li&gt;
&lt;li&gt;For &lt;code&gt;else&lt;/code&gt;, The CPU skips the first calculation and only does it once at the end.&lt;/li&gt;
&lt;li&gt;Compiler optimisation's goal is to make the work uniform so the final result is always "pre-calculated."
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (heavy_traffic):
    time = distance/speed
else:
    pass

total_time = distance/speed #why calculate the same thing a second time?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The compiler "hoists" the missing calculation into the else block:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (heavy_traffic):
    tmp = distance/speed
    time = tmp
else:
    tmp = distance/speed

#Now the final result just uses the 'tmp' already in the register
total_time = tmp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;It might look like we're adding code, but we’re actually ensuring that no matter which path the CPU takes, it only performs the heavy division exactly once.&lt;/li&gt;
&lt;li&gt;SSA tracks exactly where every value was computed. So the compiler can see at a glance, "this path did the work, that one didn't."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;7) &lt;u&gt;Strength Reduction:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Swapping out a costly operation for a cheaper one that produces the same result.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;y = x * 8
# becomes
y = x &amp;lt;&amp;lt; 3 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Multiplying by 8 is multiplying by 2³. In binary, multiplying by 2 is a left shift by one bit, so multiplying by 8 is simply a left shift by three bits.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The CPU performs this using a barrel shifter, which shifts bits in a single clock cycle. It’s basically a network of wires and switches that reroutes bits to new positions simultaneously; more like rearranging than computing.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;General multiplication is more complex. The hardware must perform multiple additions and shifts internally, requiring more circuitry and cycles.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;8) &lt;u&gt;Register Allocation:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Mapping the potentially unlimited SSA virtual variables down to the finite number of physical CPU registers available at runtime.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ex:&lt;/strong&gt; If the function produces &lt;code&gt;%tmp1&lt;/code&gt; through &lt;code&gt;%tmp20&lt;/code&gt; but the CPU only has 6 registers, the compiler figures out which temporaries work at the same time and assigns them registers accordingly, spilling the rest to the stack. Good allocation means fewer memory round-trips and faster code.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;What's next:&lt;/strong&gt; Having established some context to compiler-optimisations, we can proceed with adding support for them and JIT, for Kaleidoscope.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Musings:&lt;/strong&gt;&lt;br&gt;
Optimisation, at its core, is about simplicity. There's an incredible story from the Mahabharata about this. Dronacharya, the renowned guru, gathered his students—already among the world's finest—to find the greatest archer. The goal was the eye of a wooden bird perched high in a tree. Pointing to it, he asked each student what they saw. They described the tree, the bird’s feathers, and the sky. He dismissed them. When he asked Arjuna, the latter replied, “I only see the eye of the bird.” He shot, and the arrow struck the centre instantly. By treating everything except the target as “noise,” we focus all resources on the singular point of impact. Compiler optimisations do the same, ensuring the CPU never looks at anything but the essential logic. After all, &lt;em&gt;Neti Neti&lt;/em&gt;-ing eventually led those ancient minds to the Ultimate Answer.&lt;/p&gt;

</description>
      <category>compilers</category>
      <category>computerscience</category>
      <category>learning</category>
      <category>llvm</category>
    </item>
    <item>
      <title>LLVM #1 - Lexer, Parser, Codegen</title>
      <dc:creator>Lahari Tenneti</dc:creator>
      <pubDate>Fri, 13 Feb 2026 06:14:09 +0000</pubDate>
      <link>https://dev.to/lahari_tenneti_4a8a082e9c/llvm-1-lexer-parser-codegen-51of</link>
      <guid>https://dev.to/lahari_tenneti_4a8a082e9c/llvm-1-lexer-parser-codegen-51of</guid>
      <description>&lt;p&gt;The Lexer/Scanner and Parser aren’t very different from what we’ve done earlier. Both are initial/front-end phases of compilation, after which the code is converted into LLVM IR.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbuia113ocfszp9jyb2fy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbuia113ocfszp9jyb2fy.png" alt=" " width="772" height="1024"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I built:&lt;/strong&gt; &lt;a href="https://github.com/laharitenneti/Kaleidoscope/commits/main/src?since=2025-12-05&amp;amp;until=2026-02-09" rel="noopener noreferrer"&gt;Commits 15710fe, 2562e61&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What I understood:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1) &lt;u&gt;The Lexer:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reads raw text input one character at a time and groups them into meaningful chunks of code called Tokens.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;enum Token&lt;/code&gt; defines the kinds of words the language can process.
&lt;code&gt;gettok()&lt;/code&gt; loops through characters, skips whitespaces, recognises keywords/numbers, and handles comments too (by skipping text after &lt;code&gt;#&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;2) &lt;u&gt;AST:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Once the tokens are ready, we need to understand how they relate to each other. A tree is the best way to do the same.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ExprAST&lt;/code&gt; is the base/parent class for all the nodes.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;public: virtual ~ExprAST() = default;&lt;/code&gt;: Here, &lt;code&gt;Expr&lt;/code&gt; objects will later be manipulated through pointers to the base (&lt;code&gt;ExprAST&lt;/code&gt;) class. &lt;/li&gt;
&lt;li&gt;Without &lt;code&gt;virtual&lt;/code&gt;, if we wish to delete a subclass node, only the base class' destructor will be called, not the subclass' destructor. Any data stored in the subclass won't be deleted and can affect other areas through leaks.&lt;/li&gt;
&lt;li&gt;Thus, we use &lt;code&gt;virtual&lt;/code&gt; to delete/release all memory/resources used by the derived (sub)class.&lt;/li&gt;
&lt;li&gt;Similarly, &lt;code&gt;NumberExprAST' represents a number,&lt;/code&gt;VariableExprAST&lt;code&gt;represents a variable,&lt;/code&gt;BinaryExprAST&lt;code&gt;represents an operator with two operands,&lt;/code&gt;CallExprAST&lt;code&gt;represents a function call,&lt;/code&gt;PrototypeAST&lt;code&gt;represents a function signature (not the body), and&lt;/code&gt;FunctionAST` represents a full function (prototype + body)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;3) &lt;u&gt;The Parser:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Uses operator-precedence parsing for building AST objects for binary expressions and recursive descent parsing for everything else.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ex:&lt;/strong&gt; On seeing a number token, it creates a &lt;code&gt;NumberExprAST&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;4) &lt;u&gt;Code Generation:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This is LLVM specific. We traverse through the AST we've built and ask each node to &lt;code&gt;codegen()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ex:&lt;/strong&gt; In &lt;code&gt;NumberExprAST&lt;/code&gt;, we use &lt;code&gt;ConstantFP::get&lt;/code&gt; to create a floating-point constant in LLVM's internal format.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;NamedValues&lt;/code&gt; is a symbol table/dictionary for remembering where (in which specific memory location/register) a variable is stored.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Note:&lt;/strong&gt; LLVM uses Static Single Assignment (SSA), meaning its "virtual registers" can only be assigned once. It's why we see names like &lt;code&gt;%multmp&lt;/code&gt; and &lt;code&gt;%multmp1&lt;/code&gt; in our results.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;BinaryExprAST::codegen&lt;/code&gt; is for generating code for the left and right sides recursively. &lt;/li&gt;
&lt;li&gt;Then, we use &lt;code&gt;Builder&lt;/code&gt; to create the math instruction (like &lt;code&gt;Builder-&amp;gt;CreateFAdd&lt;/code&gt; for float addition)&lt;/li&gt;
&lt;li&gt;For &lt;code&gt;&amp;lt;&lt;/code&gt;, it converts the result to a float (0.0 or 1.0) as Kaleidoscope only uses doubles.&lt;/li&gt;
&lt;li&gt;Similarly, &lt;code&gt;FunctionAST::codegen&lt;/code&gt; creates a new function in the LLVM &lt;code&gt;Module&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Module&lt;/code&gt; here is like a project folder; a container that holds all our functions together so they can see and call each other.&lt;/li&gt;
&lt;li&gt;It creates a block called &lt;code&gt;entry&lt;/code&gt;. LLVM code lives inside such basic blocks (chunks of instructions)&lt;/li&gt;
&lt;li&gt;It tells the &lt;code&gt;Builder&lt;/code&gt; to start writing instructions into this new block.&lt;/li&gt;
&lt;li&gt;It adds the function arguments to &lt;code&gt;NamedValues&lt;/code&gt; so the body can use them.&lt;/li&gt;
&lt;li&gt;Finally, it creates a &lt;code&gt;ret&lt;/code&gt; instruction to finish the function.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;5) &lt;u&gt;Driver Code:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;MainLoop&lt;/code&gt; is an infinite loop printing &lt;code&gt;ready&amp;gt;&lt;/code&gt; waiting for the user to type.&lt;/li&gt;
&lt;li&gt;It switches based on what is typed (&lt;code&gt;def&lt;/code&gt;, `extern, or just math) and calls the appropriate handler.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;HandleDefinition&lt;/code&gt; parses the code, generates the LLVM IR, and prints it to the screen.&lt;/li&gt;
&lt;li&gt;It also sets up the operator precedence so the math logic works correctly, initialises the module, and starts the loop.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s understand this through an example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;def foo(x) x + 1&lt;/code&gt; is converted by the Lexer into &lt;code&gt;[tok_def] [tok_identifier “foo”] [ ( ] [tok_identifier “x”] [ ) ] [tok_identifier “x”] [ + ] [tok_number1]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The Parser turns it into a &lt;code&gt;FunctionAST&lt;/code&gt; object.&lt;/li&gt;
&lt;li&gt;The codegen turns that object into LLVM IR &lt;code&gt;define double @foo(double %x) { … }&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;u&gt;Results:&lt;/u&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy18g4vc7r3vxx3zwszp0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy18g4vc7r3vxx3zwszp0.png" alt=" " width="800" height="131"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuhdyb203ljte7xczdr2a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuhdyb203ljte7xczdr2a.png" alt=" " width="800" height="137"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl2df3yhosv5bh7t37jgv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl2df3yhosv5bh7t37jgv.png" alt=" " width="800" height="279"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flqmze34cy46qarhtrimb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flqmze34cy46qarhtrimb.png" alt=" " width="798" height="844"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What's next:&lt;/strong&gt; Optimising and Just-in-time (JIT) compilation!&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Musings:&lt;/strong&gt;&lt;br&gt;
The one thing that calms me the most is playing the piano. You see, mindfulness and being focused is a rather difficult thing, especially in the attention economy. So it feels particularly wonderful and refreshing when an activity demands our time, patience, and full focus, lest we compromise on the quality of its outcome. “Right hand first, left hand next, and then do it together.” I feel like every single inch of my brain is dedicated to getting that one bar right. And nothing feels more rewarding than seeing (or maybe hearing?) the fruits of our labour. Whenever I’ve felt bored, lost, confused, overwhelmed, or even happy—the piano has helped me move on feeling better. My mom wanted us to learn some or the other instrument. I was merely “okay” with the idea of learning the piano, but never could I have fathomed how important it would become to me. Life’s little surprises are often like that, with some of the most beautiful experiences coming to us when we least expect it. So it doesn’t hurt to try and be a little open.&lt;/p&gt;

</description>
      <category>llvm</category>
      <category>compilers</category>
      <category>computerscience</category>
      <category>learning</category>
    </item>
    <item>
      <title>LLVM — Introduction and Setup</title>
      <dc:creator>Lahari Tenneti</dc:creator>
      <pubDate>Wed, 11 Feb 2026 12:37:50 +0000</pubDate>
      <link>https://dev.to/lahari_tenneti_4a8a082e9c/llvm-introduction-and-setup-4c3c</link>
      <guid>https://dev.to/lahari_tenneti_4a8a082e9c/llvm-introduction-and-setup-4c3c</guid>
      <description>&lt;p&gt;Helloo! Welcome to my next project—The LLVM framework (the Kaleidoscope tutorial, to be precise). I decided to try this out as soon as I finished the jlox Interpreter in Robert Nystrom’s Crafting Interpreters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What’s LLVM?&lt;/strong&gt;&lt;br&gt;
The ‘Low Level Virtual Machine’ (LLVM) is a full compiler infrastructure that can take various programming languages, generate LLVM Intermediate Representation (IR), optimise it, and finally convert it into hardware-specific machine level code. This low level code is then run on the CPU.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why am I doing this?&lt;/strong&gt;&lt;br&gt;
The Abstract Syntax Trees (ASTs) borne out of interpreting any code are specific to its language. When we use LLVM’s IR, which is aware of hardware concepts like registers, memory, arithmetic operations, etc., our language becomes more universal.&lt;/p&gt;

&lt;p&gt;As against assembly language, which is very platform/hardware dependent, LLVM (whose IR almost resembles assembly language) supports various machines like Intel x86, ARM, RISC-V, etc. I find heterogenous hardware very fascinating, especially the prospect of hardware-agnostic compilation. That’s pretty much what drew me LLVM.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;How I set it up:&lt;/strong&gt;&lt;br&gt;
While there are many tutorials to familiarise oneself with the platform, I (surprisingly) found the documentation wonderful. Some of it I skipped, but the set-up and introduction were quite helpful. I still haven’t figured out a lot, so I’m taking things slowly. For anyone wanting to start out with LLVM, I’d recommend these:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://llvm.org/docs/GettingStarted.html#getting-the-source-code-and-building-llvm" rel="noopener noreferrer"&gt;Getting the source code and building LLVM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://llvm.org/docs/GettingStarted.html#an-example-using-the-llvm-tool-chain" rel="noopener noreferrer"&gt;An example for using the LLVM tool chain&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Disclaimer:&lt;/strong&gt; Most of what I did below is adapted from these tutorials.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;1) &lt;u&gt;Getting LLVM running&lt;/u&gt; on macOS (especially Apple Silicon) is very straightforward with &lt;a href="https://brew.sh/" rel="noopener noreferrer"&gt;Homebrew.&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;llvm
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/opt/homebrew/opt/llvm/bin:&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;CMAKE_PREFIX_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/opt/homebrew/opt/llvm"&lt;/span&gt;
&lt;span class="nb"&gt;source&lt;/span&gt; ~/.zshrc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2) &lt;u&gt;Managing Dependencies:&lt;/u&gt; I used CMake.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;llvm &lt;span class="nt"&gt;-config&lt;/span&gt; —version
&lt;span class="nb"&gt;cd&lt;/span&gt; ~
&lt;span class="nb"&gt;mkdir &lt;/span&gt;toy-compiler
&lt;span class="nb"&gt;cd &lt;/span&gt;toy-compiler
&lt;span class="nb"&gt;mkdir &lt;/span&gt;src build
nano CMakeLists.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Here's the &lt;code&gt;CMakeLists.txt&lt;/code&gt; (configuration) for the toy-compiler/Kaleidoscope project I'll be doing. It serves as a project manager, telling the compiler where the LLVM "brains" are and which specific libraries (like JIT, native codegen) we want to use. &lt;/li&gt;
&lt;li&gt;It doesn't compile the code itself, and instead gathers all the ingredients (libraries, headers, compiler settings) so the build tool (Ninja, in my case) knows what to do.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cmake"&gt;&lt;code&gt;&lt;span class="nb"&gt;cmake_minimum_required&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;VERSION 3.13&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;project&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;ToyCompiler LANGUAGES C CXX&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;CMAKE_CXX_STANDARD 17&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;CMAKE_CXX_STANDARD_REQUIRED ON&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;find_package&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;LLVM REQUIRED CONFIG&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;STATUS &lt;span class="s2"&gt;"Found LLVM &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LLVM_PACKAGE_VERSION&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;add_definitions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LLVM_DEFINITIONS&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;include_directories&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LLVM_INCLUDE_DIRS&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;link_directories&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LLVM_LIBRARY_DIRS&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;add_executable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;toy src/main.cpp&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# For JIT and native codegen&lt;/span&gt;
&lt;span class="nf"&gt;llvm_map_components_to_libnames&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;llvm_libs core orcjit native&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;target_link_libraries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;toy &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;llvm_libs&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3) &lt;u&gt;Verifying the build system:&lt;/u&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To ensure the libraries are linking correctly, I wrote a simple sanity check in &lt;code&gt;src/main.cpp&lt;/code&gt; using LLVM's output stream, &lt;code&gt;llvm::outs()&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;"llvm/Support/raw_ostream.h"&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;llvm&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;outs&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;"LLVM setup works!&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;To actually compile the project, I used Ninja. It's a small build system focused on speed.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;ninja
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/toy-compiler/build
cmake &lt;span class="nt"&gt;-G&lt;/span&gt; Ninja ..
ninja
./toy -&amp;gt; LLVM setup works!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;4) &lt;u&gt;An example:&lt;/u&gt; To truly understand how compilation happens at lower levels, it helps to follow a program through the entire pipeline, from high level code to bit code, and finally to a native binary. &lt;br&gt;
&lt;code&gt;test1.c&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"First go at LLVM!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;clang -O3 -emit-llvm test1.c -c -o test1.bc&lt;/code&gt; (Clang is the C compiler macOS uses as a front-end; &lt;code&gt;emit-llvm&lt;/code&gt; creates the bitcode.)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;lli test1.bc&lt;/code&gt; (&lt;code&gt;lli&lt;/code&gt; directly executes the bytecode.)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;llvm-dis &amp;lt; test1.bc | less&lt;/code&gt; (This is for looking at the human-readable LLVM assembly code.)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;llc test1.bc -o test1.s&lt;/code&gt; (&lt;code&gt;llc&lt;/code&gt; converts bitcode to native assembly.)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;gcc test1.s -o test1.native&lt;/code&gt; (This assembles the native file into a program.)&lt;/li&gt;
&lt;li&gt;Running &lt;code&gt;./test1.native&lt;/code&gt; → First go at LLVM!&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;What's next:&lt;/strong&gt;&lt;br&gt;
The lexer and parser for Kaleidoscope!&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Musings:&lt;/strong&gt;&lt;br&gt;
Sometimes, I get these sudden, intense urges to just... be somewhere. I often find myself wishing for Doraemon’s "Anywhere Door" so I could instantly step into a completely different world. It’s not that I don’t enjoy where I am, but there’s an incomparable high that comes from being somewhere totally foreign.&lt;/p&gt;

&lt;p&gt;Travel has always been my ultimate meditation. I once &lt;a href="https://www.stevenmtaylor.com/essays/from-the-unreal-to-the-real/#:~:text=The%20same%20thing,place%20to%20them." rel="noopener noreferrer"&gt;read&lt;/a&gt; that our memories of new places are so vivid because we become hyper-sensitive to our surroundings. In our daily lives—the same commute, the same routine—we stop truly "seeing" the world. We stop noticing the colour of the sky, the old man reading the newspaper by the shopfront, or the little puppy prancing around with an aluminium foil. But in a new place, with your senses wide open, you notice everything.&lt;/p&gt;

&lt;p&gt;A few years ago, I visited Sikkim (an absolutely stunning state in our North-East), and I’ve never felt more alive. Even now, I can recall every sound, smell, and sight from that trip with perfect clarity. That level of observation taught me how to be more attentive to the daily moments in my life. Ever since, I've tried to carry that traveler’s spirit with me, finding joy in the small details—in the "normal" and the everyday.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjucljmm85ixb9yvhnvcz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjucljmm85ixb9yvhnvcz.png" alt=" " width="800" height="414"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>llvm</category>
      <category>compilers</category>
      <category>computerscience</category>
      <category>learning</category>
    </item>
    <item>
      <title>Extensions - Lists and for-in loops</title>
      <dc:creator>Lahari Tenneti</dc:creator>
      <pubDate>Mon, 09 Feb 2026 07:18:03 +0000</pubDate>
      <link>https://dev.to/lahari_tenneti_4a8a082e9c/extensions-lists-and-for-in-loops-194f</link>
      <guid>https://dev.to/lahari_tenneti_4a8a082e9c/extensions-lists-and-for-in-loops-194f</guid>
      <description>&lt;p&gt;Well, here we are. Here’s my interpreter. I want to get philosophical already, but I’ll save it for the musings section. Anyway, I’ve extended Robert’s jlox interpreter by adding support for lists and for-in loops.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fldakyzbw3c42rpzr0kzn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fldakyzbw3c42rpzr0kzn.png" alt=" " width="800" height="530"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I built:&lt;/strong&gt; &lt;a href="https://github.com/laharitenneti/jlox-interpreter/commits/main/lahari-jlox/src/jloxinterpreter?since=2026-02-05&amp;amp;until=2026-02-06" rel="noopener noreferrer"&gt;Commits 8409dee, 1f4f09d, 380a16d, 72972f6, 64fdc5f&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What I understood:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I. Lists&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1) &lt;u&gt;The basics:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We start by adding the tokens/keywords in the &lt;code&gt;TokenType.java&lt;/code&gt; file. Because lists use square brackets, we add them (&lt;code&gt;LEFT_BRACKET&lt;/code&gt;, &lt;code&gt;RIGHT_BRACKET&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;We then update the &lt;code&gt;scanToken()&lt;/code&gt; function in the Scanner to handle the newly added tokens.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;case '[': addToken(LEFT_BRACKET); break;
case ']': addToken(RIGHT_BRACKET); break;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;We then create two types of expressions through &lt;code&gt;GenerateAst&lt;/code&gt;, one for creating a list (&lt;code&gt;Array&lt;/code&gt;) and another for accessing or modifying it (&lt;code&gt;Subscript&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;2) &lt;u&gt;Parsing for lists:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We update the &lt;code&gt;primary()&lt;/code&gt; method to catch a &lt;code&gt;LEFT_BRACKET&lt;/code&gt;, create a new list, and check for comma-separated elements until the &lt;code&gt;RIGHT_BRACKET&lt;/code&gt; is caught.&lt;/li&gt;
&lt;li&gt;We also use chaining to check for sub indices, which if found, are made into a new subscript.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;else if (match(LEFT_BRACKET)) {
    Expr index = expression();
    Token bracket = consume(RIGHT_BRACKET, "Expect ']' after index.");
    expr = new Expr.Subscript(expr, bracket, index);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7obkpbooisqnevhvnujr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7obkpbooisqnevhvnujr.png" alt=" " width="800" height="334"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;3) &lt;u&gt;Visitor methods in the Resolver:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Because we’ve added new AST nodes (Array, Subscript)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;4) &lt;u&gt;Actual List Logic:&lt;/u&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We use a java list to represent a lox list, with every expression/element inside evaluated and stored in the lox list. The SubscriptExpr’s job is to check whether the variable we’re trying to index is actually a list, if the index is a number and within the list size.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvnn5lxqcta4n9tq5y0y9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvnn5lxqcta4n9tq5y0y9.png" alt=" " width="800" height="351"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;5) &lt;u&gt;List functions:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Finally, we implement global native functions that act as an interface between the user and the Java &lt;code&gt;ArrayList&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;push(list, item)&lt;/code&gt; calls &lt;code&gt;add()&lt;/code&gt; on the java list to append an item to the array.&lt;/li&gt;
&lt;li&gt;pop(list)  finds the size() - 1th element and uses java’s &lt;code&gt;remove()&lt;/code&gt; method to delete it (the last item) from the list and return it.&lt;/li&gt;
&lt;li&gt;Likewise, len(list) uses java’s size() function to fetch the array’s length.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgyhzwlw5p2jl5hkp2k5a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgyhzwlw5p2jl5hkp2k5a.png" alt=" " width="800" height="330"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;II. For-in loops&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1) &lt;u&gt;The basics:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add the &lt;code&gt;IN&lt;/code&gt; token to the enum in &lt;code&gt;TokenType.java&lt;/code&gt; and &lt;code&gt;in&lt;/code&gt; to the keywords map in the Scanner.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;checkNext()&lt;/code&gt; is a helper function we use to look ahead for an &lt;code&gt;in&lt;/code&gt; keyword when we encounter &lt;code&gt;for (var i …&lt;/code&gt;. If found, the Parser will build a list-iterator. If &lt;code&gt;in&lt;/code&gt; is not found, it is considered a normal for-loop.&lt;/li&gt;
&lt;li&gt;Next, update the &lt;code&gt;forStatement()&lt;/code&gt; function in the Parser with a check for &lt;code&gt;in&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;2) &lt;u&gt;Desugaring:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We then desugar for the parser to break down the complex and user-friendly syntax into simpler pieces the interpreter can handle (primitives). &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Side note:&lt;/strong&gt; Desugaring is also a life-lesson. Breaking down complex problems into smaller and simpler ideas always improves the possibility of solving them. Richard Feynman all the way!&lt;/li&gt;
&lt;li&gt;It creates two hidden variables - &lt;code&gt;_list&lt;/code&gt; and &lt;code&gt;_i&lt;/code&gt; to store the collection and counter (starting at 0.0) respectively.&lt;/li&gt;
&lt;li&gt;The Parser then creates an AST that resembles a &lt;code&gt;while&lt;/code&gt; loop. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ex:&lt;/strong&gt; &lt;code&gt;for (var item in myList) { print item; }&lt;/code&gt; effectively becomes:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    var _list = myList; 
    var _i = 0;
    while (_i &amp;lt; len(_list)) {
        {
            var item = _list[_i]; // The "item" variable is created here
            print item;           // The original body
        }
    _i = _i + 1;            // The increment
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;listVar&lt;/code&gt; and &lt;code&gt;itemVar&lt;/code&gt; in the &lt;code&gt;forStatement()&lt;/code&gt; are initialisers,  which occur once before the loop begins.&lt;/li&gt;
&lt;li&gt;The forStatement() creates a variable meant for the user (like &lt;code&gt;item&lt;/code&gt;) and uses &lt;code&gt;Expr.Subscript&lt;/code&gt; to fetch its value from &lt;code&gt;_list&lt;/code&gt; at index &lt;code&gt;_i&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The next block increments by adding 1 to &lt;code&gt;_i&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The condition block calls the native &lt;code&gt;len()&lt;/code&gt; function to check if the index &lt;code&gt;_i&lt;/code&gt; is still less than the list size.&lt;/li&gt;
&lt;li&gt;We use curly braces for scope, to ensure that the hidden variables &lt;code&gt;_list&lt;/code&gt; and &lt;code&gt;_i&lt;/code&gt; are destroyed as soon as the loop ends. Else, trying to use &lt;code&gt;_list&lt;/code&gt; later in the program might confuse the interpreter as it was never previously defined.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fskjqayof0zsbuz1cg2f3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fskjqayof0zsbuz1cg2f3.png" alt=" " width="800" height="262"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Musings:&lt;/strong&gt;&lt;br&gt;
That brings the interpreter to an end. Not “The End,” (because I’m going to keep extending it) but you could say all that I’d intended to do for this project has been accomplished. I took on something that puzzled me, sat and reasoned with it, learnt from it, and finally overcame my hesitation towards it. I now want to get into the “proper” backend of compilation.&lt;/p&gt;

&lt;p&gt;The first step is always the hardest one. But we must take it anyway. Mistakes will happen and failing is certain. But rarely will we regret it, if we learn how to learn from our experiences. I’ve taken risks that didn’t yield what I hoped for. It was a little disappointing, but they’ve 100% made me a better person—well informed and sensitive to issues that matter. &lt;br&gt;
This project coincided with such a phase in life. Amidst confusion and uncertainty, it gave me something I could keep coming back to consistently; something I could see tangible progress in; something that gave me the satisfaction of “creating.” We see programming languages all the time, but know so little of all the thought and effort going into them. Every design choice is intended to make programming easier and more intuitive; more accessible too! If I had to sum up in a sentence what I learnt from this—&lt;em&gt;“God is in the details.”&lt;/em&gt; I will always be grateful for having done this. &lt;/p&gt;

&lt;p&gt;Dear reader, if you’ve followed this project thus far, thank you for your time! Until next time, for the next adventure! &lt;/p&gt;

</description>
      <category>compilers</category>
      <category>interpreter</category>
      <category>computerscience</category>
      <category>learning</category>
    </item>
    <item>
      <title>Classes and Inheritance</title>
      <dc:creator>Lahari Tenneti</dc:creator>
      <pubDate>Wed, 04 Feb 2026 11:02:31 +0000</pubDate>
      <link>https://dev.to/lahari_tenneti_4a8a082e9c/classes-and-inheritance-495j</link>
      <guid>https://dev.to/lahari_tenneti_4a8a082e9c/classes-and-inheritance-495j</guid>
      <description>&lt;p&gt;We now move on to complete the interpreter by adding support for classes. They will also be able to inherit from other classes (i.e., reuse their properties and methods) while also customising them through modifications and additions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fts00ib2tnqm34hx7h6kw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fts00ib2tnqm34hx7h6kw.png" alt=" " width="800" height="1234"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I built:&lt;/strong&gt; &lt;a href="https://github.com/laharitenneti/jlox-interpreter/commits/main/?since=2025-10-21&amp;amp;until=2025-10-31" rel="noopener noreferrer"&gt;Commits bac3f3c, bfa82b9, 03b3af3, 6c70087&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What I understood:&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;1) &lt;u&gt;Class Declarations:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The parser creates a &lt;code&gt;Stmt.Class&lt;/code&gt; node for each class declaration, containing the name and list of methods.&lt;/li&gt;
&lt;li&gt;The resolver declares the class name in the current scope to allow the class to refer to itself (inside its own methods), for recursion.&lt;/li&gt;
&lt;li&gt;When the interpreter visits &lt;code&gt;Stmt.Class&lt;/code&gt;, it converts the AST node into a &lt;code&gt;LoxClass&lt;/code&gt; object, which stores a map of methods (which are in turn converted into &lt;code&gt;LoxFunction&lt;/code&gt; objects). &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;2) &lt;u&gt;Class Instances/Objects:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lox doesn’t use any &lt;code&gt;new&lt;/code&gt; keyword.&lt;/li&gt;
&lt;li&gt;Instead, &lt;code&gt;LoxClass&lt;/code&gt; implements the &lt;code&gt;LoxCallable&lt;/code&gt; interface such that a class is instantiated whenever called.&lt;/li&gt;
&lt;li&gt;When the class is “called,” a new &lt;code&gt;LoxInstance&lt;/code&gt; object is instantiated and returned. &lt;/li&gt;
&lt;li&gt;This new object stores a reference to its class (&lt;code&gt;LoxClass&lt;/code&gt;) so it can access methods later.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;3) &lt;u&gt;Properties:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fields aren’t declared in the class, but are created on assignment through instances.&lt;/li&gt;
&lt;li&gt;When the interpreter evaluates that an object is a &lt;code&gt;LoxInstance&lt;/code&gt;, it first calls &lt;code&gt;get()&lt;/code&gt;, which checks the instance’s field map.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If the field exists, the value is returned. Else, it looks for a method. If neither are found, it throws a runtime error.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Also, the parser can’t easily distinguish between a set and get expression until it encounters a &lt;code&gt;=&lt;/code&gt; sign. Thus, it parses the left-hand side as a regular expression (a &lt;code&gt;get&lt;/code&gt;), and on encountering the &lt;code&gt;=&lt;/code&gt; it converts that node into a &lt;code&gt;set&lt;/code&gt; node.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If &lt;code&gt;set()&lt;/code&gt; is invoked, the &lt;code&gt;fields&lt;/code&gt; map is updated to overwrite any existing key.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;4) &lt;u&gt;Methods:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lox methods don’t have the &lt;code&gt;fun&lt;/code&gt; keyword preceding them and are accessed through instances.&lt;/li&gt;
&lt;li&gt;If we extract a method (assign it to a variable) and call it later, &lt;code&gt;this&lt;/code&gt; must still refer to the instance the method was accessed/extracted from, and not where it was called.&lt;/li&gt;
&lt;li&gt;To achieve this, when a method is accessed, &lt;code&gt;LoxClass&lt;/code&gt; instead of returning the raw &lt;code&gt;LoxFunction&lt;/code&gt;, calls &lt;code&gt;bind(instance)&lt;/code&gt; on it.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;bind()&lt;/code&gt; creates a new environment nested inside the method’s original closure, defines &lt;code&gt;this&lt;/code&gt; in that new environment, and uses it to return a new &lt;code&gt;LoxFunction&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;If &lt;code&gt;this&lt;/code&gt; is used outside a class, the resolver (which tracks if it’s currently inside a class) reports an error. Else, it resolves &lt;code&gt;this&lt;/code&gt; as a local variable.&lt;/li&gt;
&lt;li&gt;The interpreter looks up &lt;code&gt;this&lt;/code&gt; in the environment like any other variable. Because of the binding step, the correct instance is found.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1nwn4scb53kuewihcgut.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1nwn4scb53kuewihcgut.png" alt=" " width="800" height="438"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;5) &lt;u&gt;Constructors:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We implement support for user-defined constructors called &lt;code&gt;init&lt;/code&gt;, which if found (by &lt;code&gt;LoxClass.call&lt;/code&gt;), is called immediately to set up the new instance. Also, arity is checked here to ensure the class’ argument count matches the initialiser’s.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;init&lt;/code&gt; always returns &lt;code&gt;this&lt;/code&gt; (even if the user tries to return any value from inside the function). The resolver ensures this by tracking through an &lt;code&gt;isInitialiser&lt;/code&gt; flag in &lt;code&gt;LoxFunction&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;6) &lt;u&gt;Inheritance:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Allows a subclass to inherit methods from a superclass through a reference to the latter (stored by the subclass’ &lt;code&gt;LoxClass&lt;/code&gt;) and a method lookup walking up the chain.&lt;/li&gt;
&lt;li&gt;The parser creates a &lt;code&gt;Stmt.Class&lt;/code&gt; node that now includes a &lt;code&gt;superclass&lt;/code&gt; variable (of type &lt;code&gt;Expr.Variable&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;The resolver checks if a class tries to inherit from itself and reports an error if yes.&lt;/li&gt;
&lt;li&gt;If a superclass exists, the resolver creates a new scope and defines &lt;code&gt;super&lt;/code&gt; in it, ensuring &lt;code&gt;super&lt;/code&gt; is available to all methods in the subclass.&lt;/li&gt;
&lt;li&gt;While creating a subclass, the interpreter creates a new environment, defines &lt;code&gt;super&lt;/code&gt; to point to the superclass, and then creates the methods.&lt;/li&gt;
&lt;li&gt;The parser recursively looks up a method by first checking the instance’s fields, then the class’ methods, and then the superclass, which if null, causes the lookup to fail.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fucafxqk8gaix3iou4h7h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fucafxqk8gaix3iou4h7h.png" alt=" " width="800" height="530"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;7) &lt;u&gt;&lt;code&gt;Super&lt;/code&gt;:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This keyword allows subclasses to override (their own version of) a method and access the original implementation in a parent class.&lt;/li&gt;
&lt;li&gt;It ensures that even if a chain of subclasses overrides a method, &lt;code&gt;super&lt;/code&gt; will always point to the correct parent class definition.&lt;/li&gt;
&lt;li&gt;It is followed by a dot and an identifier (&lt;code&gt;super.method&lt;/code&gt;) and is parsed as an &lt;code&gt;Expr.Super&lt;/code&gt; node.&lt;/li&gt;
&lt;li&gt;It looks up a method on the superclass but binds it to the current instance (&lt;code&gt;this&lt;/code&gt;), with both belonging to different environments.&lt;/li&gt;
&lt;li&gt;The resolver treats &lt;code&gt;super&lt;/code&gt; as a local variable and calculates its distance (number of hops) from the environment in which &lt;code&gt;super&lt;/code&gt; is defined.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fta2fd8ho8sa1zlbs3vj2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fta2fd8ho8sa1zlbs3vj2.png" alt=" " width="800" height="826"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What's next:&lt;/strong&gt; Some extensions to Lox. Support for lists and for-in loops.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Musings:&lt;/strong&gt;&lt;br&gt;
Blogging this project has been really good for me. It probably is because despite doing other projects at the moment, trying to explain it to myself by writing encourages me to remember what I did (months ago!) and why. In a way, the insights I gained earlier continue to influence how I approach my current work. I absolutely love documenting things I do, even if it's for the most mundane of activities. In Indian philosophy, work is among the highest acts of devotion. Whatever we do, if we do it with intent, it leads us to competence and mastery. Because it's the process of learning and doing something that helps us become our best versions. Tagore put it well — "Where tireless striving stretches its arms towards perfection..."&lt;br&gt;
In pursuit of that perfection, I remind myself of all that I've learnt.&lt;/p&gt;

</description>
      <category>compilers</category>
      <category>interpreter</category>
      <category>computerscience</category>
      <category>learning</category>
    </item>
    <item>
      <title>Functions</title>
      <dc:creator>Lahari Tenneti</dc:creator>
      <pubDate>Mon, 05 Jan 2026 07:04:47 +0000</pubDate>
      <link>https://dev.to/lahari_tenneti_4a8a082e9c/functions-2pmi</link>
      <guid>https://dev.to/lahari_tenneti_4a8a082e9c/functions-2pmi</guid>
      <description>&lt;p&gt;Well, It's been a minute. I'd finished the interpreter a long time ago, but had put off blogging it because a thousand different things came up these past few months. Anyhow, it now takes on the ability to handle functions - user defined, native, and nested - and even deal with shadowing, wherein a variable in a local scope "eclipses" the same (existing) variable in an outer scope. We'll also enter proper compiler territory by creating a "resolver" phase.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fricyxk0j91x74ci4o0li.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fricyxk0j91x74ci4o0li.png" alt=" " width="783" height="1024"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv2i9zx9hwqvvjw2oosh4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv2i9zx9hwqvvjw2oosh4.png" alt=" " width="800" height="749"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I built:&lt;/strong&gt; &lt;a href="https://github.com/laharitenneti/jlox-interpreter/commits/main/lahari-jlox/src/jloxinterpreter?since=2025-10-07&amp;amp;until=2025-10-15" rel="noopener noreferrer"&gt;Commits 3da669d, 930014e, 5adb148, and 9d534f3&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What I understood:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1) &lt;u&gt;Updating the Grammar:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The function call takes the highest precedence here, above the unary rule.&lt;/li&gt;
&lt;li&gt;We also create an object (class) called &lt;code&gt;Callable&lt;/code&gt; that helps resolve whether or not something can be used as a function.&lt;/li&gt;
&lt;li&gt;Ex: A string isn't callable, while classes and functions (user-defined and native) are. Python does something really cool by allowing all its datatypes to be callable, because they're really objects that're being called.&lt;/li&gt;
&lt;li&gt;If the "callee" (entity calling the function) doesn't belong to &lt;code&gt;LoxCallable&lt;/code&gt;, Lox raises an error.&lt;/li&gt;
&lt;li&gt;The parser also allows for currying or chained function calls through &lt;code&gt;(( "(" arguments? ")" )*&lt;/code&gt;, allowing for &lt;code&gt;function(arg)(arg)(arg)&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9gyailgxdcxmkkz9m1jm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9gyailgxdcxmkkz9m1jm.png" alt=" " width="800" height="712"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;2) &lt;u&gt;Arguments:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We also check for arity, wherein the number of parameters in the function definition must equal the number of arguments passed in the call.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqgjai2caydn65d48jfiz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqgjai2caydn65d48jfiz.png" alt=" " width="800" height="347"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;3) &lt;u&gt;Native Functions:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We create &lt;code&gt;clock()&lt;/code&gt; in Java, which Lox can access.&lt;/li&gt;
&lt;li&gt;This is an anonymous class implementing &lt;code&gt;LoxCallable&lt;/code&gt;, instantiated and bound to a variable "clock" in the interpreter's globals environment.&lt;/li&gt;
&lt;li&gt;It returns the system time in seconds, for us to perform benchmarks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;4) &lt;u&gt;Function Declaration:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every time the interpreter encounters &lt;code&gt;fun&lt;/code&gt;, it creates a callable object out of it (through &lt;code&gt;LoxCallable&lt;/code&gt;) and binds it to the function's name in a new environment created.&lt;/li&gt;
&lt;li&gt;In this local environment, the function's arguments are bound to the parameters mentioned in the declaration.&lt;/li&gt;
&lt;li&gt;Every time the interpreter encounters a function call, a new environment is created. This is to allow for recursion, which wouldn't be possible if an environment existed only for declarations.&lt;/li&gt;
&lt;li&gt;Once the function's body is completed, this local environment is destroyed and the execution environment returns to the state before the call.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;5) &lt;u&gt;Return:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The interpreter looks for a return statement followed by an optional expression.&lt;/li&gt;
&lt;li&gt;If no return is mentioned, &lt;code&gt;nil&lt;/code&gt; is implicitly returned.&lt;/li&gt;
&lt;li&gt;When &lt;code&gt;return&lt;/code&gt; is encountered, the interpreter must immediately jump out of all its (current) environments and revert to the original function call site.&lt;/li&gt;
&lt;li&gt;Instead of a complex stack to do so, we use a simple exception class called &lt;code&gt;Return&lt;/code&gt;, which evaluates the return value (if given) and wraps it, finally returning it as the function's result.&lt;/li&gt;
&lt;li&gt;This is kept inside a try-catch block, that looks for the return "exception", which if not found, causes the full function to be parsed and &lt;code&gt;nil&lt;/code&gt; returned implicitly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;6) &lt;u&gt;Scoping:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;So far, Lox was able to support dynamic scoping wherein a called function's scope was always set to the global environment, and not the environment it was defined in. &lt;/li&gt;
&lt;li&gt;This hindered the called function's access to variables in the environment it was defined in, once the environment ended (when the function finished executing).&lt;/li&gt;
&lt;li&gt;Basically, a live reference to a mutable scope was being captured, when it should have captured the lexical environment it was defined in, instead of deferring name resolution to the call site.&lt;/li&gt;
&lt;li&gt;To explain this with an example:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fun getName(name) {
     var person = name;
     fun greet() {
          print “Hello, “ + person + “!”;
     }
     return greet;
}
var momo = getName("Momo”);
var chutney = getName("Chutney”);

momo(); //Should give “Hello, Momo!” ; Instead, it gives an error: “Undefined variable!”
chutney(); //Should give “Hello, Chutney!”
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;This is because once &lt;code&gt;getName("Momo") finishes, the variable&lt;/code&gt;person` is destroyed, as the function's scope ended.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When &lt;code&gt;momo()&lt;/code&gt; is called, it tries to find &lt;code&gt;person&lt;/code&gt; in its parent scope, which has now become the global scope (which doesn't contain the variable &lt;code&gt;person&lt;/code&gt;) → So error!&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We deal with this issue of dynamic scoping, by using lexical scoping.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Here, when a function is first declared, we close over and capture the environment surrounding the function (inner function holds a live reference to the outer one, which the garbage collector can't destroy despite the completion of the function's execution).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When the function is called, this captured environment becomes the call's parent instead of &lt;code&gt;globals&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;7) &lt;u&gt;Resolver:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Implementing closures through lexical scoping gave rise to another issue.&lt;/li&gt;
&lt;li&gt;Because a closure holds a live reference to a "mutable" map representing the current scope, its captured view of the current scope can change based on code that runs later in the same block.&lt;/li&gt;
&lt;li&gt;This shouldn't be the case. Because Lox has lexical/static scope, a variable's usage must always "resolve" to the same declaration throughout the program's execution.&lt;/li&gt;
&lt;li&gt;Even a redeclaration/redefinition of the variable later in the code shouldn't influence behaviour written with the original/declared value in mind.&lt;/li&gt;
&lt;li&gt;Let's understand this through an example:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;&lt;br&gt;
var a = "one";&lt;br&gt;
{&lt;br&gt;
    fun showA() {&lt;br&gt;
        print a;&lt;br&gt;
    }&lt;br&gt;
    showA();&lt;br&gt;
    var a = "two";&lt;br&gt;
    showA();&lt;br&gt;
}&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In this code, the variable &lt;code&gt;a&lt;/code&gt; belongs to the global scope, with its value being "one."&lt;/li&gt;
&lt;li&gt;The function &lt;code&gt;showA&lt;/code&gt; creates a closure that captures the block/environment it is declared in.&lt;/li&gt;
&lt;li&gt;When &lt;code&gt;showA()&lt;/code&gt; is called, &lt;code&gt;a&lt;/code&gt; is first searched for in the block, and when not found, is searched for in the environment above (i.e., global). Hence, the output for &lt;code&gt;print a&lt;/code&gt; is "one."&lt;/li&gt;
&lt;li&gt;But, &lt;code&gt;a&lt;/code&gt; is redeclared to have the value "two" in the same block (i.e., in the same environment captured by &lt;code&gt;showA()&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;p&gt;So, when &lt;code&gt;showA()&lt;/code&gt; is called after the redeclaration, because the scope was mutable, the output is "two" when it should've been "one" again.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The resolver, added after the parser and before the interpreter, thus uses a concept called "Persistent Data Structures" wherein the original data structure can't be tampered with directly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Any change to an existing structure creates a newer structure that contains the original information along with the modification.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;To give a parallel, this is how blockchain works. If you're familiar with it, you must know that every time a block is modified in a ledger, a new block is created referencing the older one's hash, while also containing the change.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When the resolver enters a block and sees a variable being "used" (after its declaration), it searches upward from the function's scope.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;&lt;br&gt;
Scope 0 (the function's scope):&lt;/code&gt;a&lt;code&gt;is not found&lt;br&gt;
Scope 1 (block scope):&lt;/code&gt;a&lt;code&gt;is not found (as&lt;/code&gt;var a = "two"&lt;code&gt;hasn't been defined yet in the resolver's static pass)&lt;br&gt;
Scope 2 (global scope):&lt;/code&gt;a&lt;code&gt;is found&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Because the variable is found 2 hops away from the innermost scope (of its use), the resolver annotates that "use" or node with the number 2. &lt;/li&gt;
&lt;li&gt;When the interpreter encounters &lt;code&gt;print a&lt;/code&gt;, instead of searching for the variable, it just uses the given number of hops to jump to the declaration.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;What's next:&lt;/strong&gt; We're going to keep it Classy! 💅&lt;br&gt;
(Hehe, classes and inheritance, in case you didn't get it)&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Musings:&lt;/strong&gt;&lt;br&gt;
I’ve had the opportunity to witness some of the most wonderful and bewildering of things these past few months. Feels like I saw the full spectrum of life, much like Shakespeare’s seven ages of man. In a way, it really humbled me. For the longest time, I was afraid of letting go… of my beliefs, interests, and even some dreams, because I thought I’d lose my very identity. Now, I’ve learnt that much like functions, it’s okay to have a template of non-negotiables, upon which we can let life keep adding her own “implementations.” (I absolutely HAD to use that analogy, pleeease excuse me). Change and unpredictability don’t seem so daunting anymore, and maybe (like 0.05%) are good too. I suppose c’est la vie.&lt;/p&gt;

</description>
      <category>compilers</category>
      <category>interpreter</category>
      <category>computerscience</category>
      <category>learning</category>
    </item>
    <item>
      <title>Logical Operators &amp; Control Flow</title>
      <dc:creator>Lahari Tenneti</dc:creator>
      <pubDate>Fri, 10 Oct 2025 09:36:25 +0000</pubDate>
      <link>https://dev.to/lahari_tenneti_4a8a082e9c/logical-operators-control-flow-2oa5</link>
      <guid>https://dev.to/lahari_tenneti_4a8a082e9c/logical-operators-control-flow-2oa5</guid>
      <description>&lt;p&gt;After statements, we will now be adding support for logical operators and control flow statements - conditional (if-else) and looping (while, for).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0kaaht0gn7cv9lrb5ty5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0kaaht0gn7cv9lrb5ty5.png" alt=" " width="800" height="1050"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I built:&lt;/strong&gt; &lt;a href="https://github.com/laharitenneti/jlox-interpreter/commit/089ad4dc658ef699b10bb50ebbe50d1660dfb6b4" rel="noopener noreferrer"&gt;Commit 089ad4d&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What I understood:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1) If Statements&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We extend the &lt;code&gt;statement&lt;/code&gt; rule to accomodate &lt;code&gt;ifStmt&lt;/code&gt;, which leads to &lt;code&gt;expression()&lt;/code&gt;. The &lt;code&gt;thenBranch&lt;/code&gt; calls &lt;code&gt;statement()&lt;/code&gt; again.&lt;/li&gt;
&lt;li&gt;Also, we avoid the &lt;em&gt;dangling-else problem&lt;/em&gt;, wherein nesting-if conditions cause uncertainty about which &lt;code&gt;if&lt;/code&gt; statement an &lt;code&gt;else&lt;/code&gt; clause belongs to - inner or outer?&lt;/li&gt;
&lt;li&gt;This is by checking for the existence of an &lt;code&gt;else&lt;/code&gt; branch which is assumed to belong to the innermost &lt;code&gt;if&lt;/code&gt; statement.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;2) Logical Operators&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We modify the grammar to have &lt;code&gt;assignment()&lt;/code&gt; call &lt;code&gt;or()&lt;/code&gt; (which calls &lt;code&gt;and()&lt;/code&gt;) instead of &lt;code&gt;equality()&lt;/code&gt;. &lt;code&gt;equality()&lt;/code&gt; is called by &lt;code&gt;and()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;We use short-circuiting while using logical-operators, which makes decisions basis the state of the left-operand.&lt;/li&gt;
&lt;li&gt;Ex: In &lt;code&gt;OR&lt;/code&gt;, if the left operand is truthy (evaluates to true), the interpreter skips the right operand, and returns the left operand itself.&lt;/li&gt;
&lt;li&gt;But if the left-operand is &lt;code&gt;false&lt;/code&gt;, it has to check the right operand and compute the result based on it.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var greet = greeting or "Hello";
//defaults to Hello if greeting is null/false.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;In &lt;code&gt;AND&lt;/code&gt;, if the left-operand is &lt;code&gt;false&lt;/code&gt;, the entire expression results in &lt;code&gt;false&lt;/code&gt;, and thus the second operand needn't be evaluated.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//LOGIC  
public Object visitLogicalExpr(Expr.Logical expr) {
    Object left = evaluate(expr.left);

    if (expr.operator.type == TokenType.OR) {
      if (isTruthy(left)) return left;
    } else {
      if (!isTruthy(left)) return left;
    }

    return evaluate(expr.right);
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1q2asictnfsmyuczwatl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1q2asictnfsmyuczwatl.png" alt=" " width="800" height="782"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;3) While Loops&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;statement&lt;/code&gt; rule is further extended to call &lt;code&gt;whileStmt&lt;/code&gt;, which basically executes the body of statements if the given condition evaluates to ‘true’
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//LOGIC
public Void visitWhileStmt(Stmt.While stmt) {
    while (isTruthy(evaluate(stmt.condition))) {
      execute(stmt.body);
    }
    return null;
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx0kt162gqp9gprinv7ra.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx0kt162gqp9gprinv7ra.png" alt=" " width="800" height="558"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;4) For Loops&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;forStmt&lt;/code&gt; is added to the &lt;code&gt;statement&lt;/code&gt; rule. A for condition is supposed to have three parts - initialiser, the condition, and the action (increment/decrement). Variables are created to hold these three parts.&lt;/li&gt;
&lt;li&gt;Interestingly, we don’t evaluate a &lt;code&gt;for&lt;/code&gt; loop as a &lt;code&gt;for&lt;/code&gt; loop, and actually convert it into a &lt;code&gt;while&lt;/code&gt; loop&lt;/li&gt;
&lt;li&gt;We first check for the initialiser by looking for &lt;code&gt;VAR&lt;/code&gt; and call &lt;code&gt;varDeclaration()&lt;/code&gt; if it’s present.&lt;/li&gt;
&lt;li&gt;Then, the interpreter looks for a &lt;code&gt;;&lt;/code&gt;, after which it calls &lt;code&gt;expression()&lt;/code&gt;. Finally, we call &lt;code&gt;expression()&lt;/code&gt; again on encountering an increment/decrement.&lt;/li&gt;
&lt;li&gt;The statements are wrapped inside a block, as a list.&lt;/li&gt;
&lt;li&gt;We also append the increment condition to the body of statements.&lt;/li&gt;
&lt;li&gt;When then wrap this body into a &lt;code&gt;while&lt;/code&gt; loop, that is executed after the initialiser statement runs exactly once.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for (var i = 0; i &amp;lt; 10; i = i + 1) print i;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;is converted to&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var i = 0; // Initialiser runs once 
while (i &amp;lt; 10) {
// the parser here groups the original body of statements and the
increment into a single unit for the while loop’s body
    {
        print i;
        i = i + 1; 
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpt79b2z8zbxy4zjz6yai.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpt79b2z8zbxy4zjz6yai.png" alt=" " width="800" height="587"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What's next:&lt;/strong&gt;&lt;br&gt;
Functions! This takes the interpreter ahead significantly by introducing concepts like lexical scope, closures, call stack management, and a return mechanism.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Musings:&lt;/strong&gt;&lt;br&gt;
Coffee Cold is a song I deeply love. For some reason, it feels like both a dig at and a celebration of life’s strangeness. Like it’s trying to tell me that at the end of the day, there are things way beyond my control that shape my world. I can either choose to get upset due to them or smile and walk on, towards my next adventure. “Life is neither a &lt;em&gt;Tempest&lt;/em&gt;, nor a &lt;em&gt;Midsummer Night’s Dream&lt;/em&gt;. It’s more like a &lt;em&gt;Comedy of Errors&lt;/em&gt; and you take it &lt;em&gt;As You Like It&lt;/em&gt;.” As the Tao Te Ching says, it's alllll about the &lt;strong&gt;flow&lt;/strong&gt;. 😉&lt;/p&gt;

</description>
      <category>compilers</category>
      <category>interpreter</category>
      <category>computerscience</category>
      <category>learning</category>
    </item>
    <item>
      <title>Evaluating Statements</title>
      <dc:creator>Lahari Tenneti</dc:creator>
      <pubDate>Thu, 02 Oct 2025 17:32:13 +0000</pubDate>
      <link>https://dev.to/lahari_tenneti_4a8a082e9c/evaluating-statements-3m1j</link>
      <guid>https://dev.to/lahari_tenneti_4a8a082e9c/evaluating-statements-3m1j</guid>
      <description>&lt;p&gt;So far, we evaluated expressions. Now, we'll be interpreting statements and adding support for variables, assignment, and blocks.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyjd5184g9h0wm1dsyd2v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyjd5184g9h0wm1dsyd2v.png" alt=" " width="800" height="767"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I built:&lt;/strong&gt; &lt;a href="https://github.com/laharitenneti/jlox-interpreter/commit/e04ea1dfef284400597a512dbedd1f89f4e89b3e" rel="noopener noreferrer"&gt;Commit e04ea1d&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What I understood:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1) &lt;u&gt;Summary:&lt;/u&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Without any "memory," an interpreter is merely a glorified calculator. To really work, it should be able to remember variables and their values - basically storing them somewhere.&lt;/li&gt;
&lt;li&gt;As against expressions - which &lt;strong&gt;evaluate&lt;/strong&gt; to something - statements can &lt;strong&gt;execute&lt;/strong&gt; an action, and can change the state of the world outside them.&lt;/li&gt;
&lt;li&gt;We extend our grammar to now begin with &lt;code&gt;declaration&lt;/code&gt; as the top-level rule, which refers to the rules &lt;code&gt;varDecl&lt;/code&gt; or &lt;code&gt;statement&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;statement&lt;/code&gt; follows two other rules, &lt;code&gt;exprStmt&lt;/code&gt; or &lt;code&gt;printStmt&lt;/code&gt; to add support for print (which isn't a function in lox) and other kinds of statements.&lt;/li&gt;
&lt;li&gt;Like &lt;code&gt;Expr&lt;/code&gt;, we create a &lt;code&gt;Stmt&lt;/code&gt; class using &lt;code&gt;GenerateAst&lt;/code&gt;, which has the subclasses &lt;code&gt;Block&lt;/code&gt;, &lt;code&gt;Expression&lt;/code&gt;, &lt;code&gt;Print&lt;/code&gt;, and &lt;code&gt;Var&lt;/code&gt; - each with their own &lt;code&gt;visitSubclass()&lt;/code&gt; methods to be overridden by the &lt;code&gt;Interpreter&lt;/code&gt; class.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;2) &lt;u&gt;Flow:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;run()&lt;/code&gt; calls &lt;code&gt;parse()&lt;/code&gt;, which repeatedly calls &lt;code&gt;declaration()&lt;/code&gt; to process the Lox script line by line.&lt;/li&gt;
&lt;li&gt;The parser starts building the AST as it consumes tokens and first creates &lt;code&gt;List&amp;lt;Stmt&amp;gt;&lt;/code&gt; (list of statements in the program)&lt;/li&gt;
&lt;li&gt;It first looks for a &lt;code&gt;VAR&lt;/code&gt; token, which if not found, causes it to call &lt;code&gt;statement()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;When it encounters a block, it enters and starts parsing the block’s contents as well.&lt;/li&gt;
&lt;li&gt;Similar to the contents within &lt;code&gt;()&lt;/code&gt; being treated as one entity, the entire contents of a block (within &lt;code&gt;{}&lt;/code&gt;) are treated as a statement.&lt;/li&gt;
&lt;li&gt;It also uses &lt;code&gt;peek()&lt;/code&gt;, when a &lt;code&gt;=&lt;/code&gt; is encountered, to check whether it is an expression or assignment.&lt;/li&gt;
&lt;li&gt;Also, it checks whether or not the left-side of the &lt;code&gt;=&lt;/code&gt; is an assignable target.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ex:&lt;/strong&gt; &lt;code&gt;a + b = 5&lt;/code&gt; is not an assignment; it is an expression&lt;/li&gt;
&lt;li&gt;REPL:
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Folgevktm0kcjj0vxtukr.png" alt=" " width="800" height="323"&gt;
&lt;/li&gt;
&lt;li&gt;The Interpreter uses a hash-map called &lt;code&gt;Environment&lt;/code&gt; to keep track of the declared variables.&lt;/li&gt;
&lt;li&gt;There are two methods, namely &lt;code&gt;define()&lt;/code&gt; and &lt;code&gt;assign()&lt;/code&gt; for dealing with variables.&lt;/li&gt;
&lt;li&gt;When we’re defining/declaring/creating a variable, &lt;code&gt;Stmt&lt;/code&gt;’s &lt;code&gt;Var&lt;/code&gt; subclass operates on the current environment.&lt;/li&gt;
&lt;li&gt;Whenever we update a variable, &lt;code&gt;assign()&lt;/code&gt; searches up the enclosing chain recursively, until it finds the mentioned variable; In that scope, it updates the variable. &lt;code&gt;Expr&lt;/code&gt;’s &lt;code&gt;Assign&lt;/code&gt; subclass uses this method.&lt;/li&gt;
&lt;li&gt;Whenever a local variable is created (i.e., a new environment), it also keeps track of its parent environment.
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgq5c5my8q102rwwfm0td.jpg" alt=" " width="800" height="850"&gt;
&lt;/li&gt;
&lt;li&gt;For print statements, it performs concatenation if more than one (string) token is involved.
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyspg6eb0t7soqd8i9smz.jpg" alt=" " width="800" height="290"&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Let's understand this through an example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var name = "World";
{
  var greeting = "Hello, ";
  print greeting + name;
  name = "Lox";
}
print name;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The parser creates a list of statements, in which each item is further parsed for tokens.&lt;/li&gt;
&lt;li&gt;In Line 1, &lt;code&gt;var&lt;/code&gt; is encountered, leading to the rule &lt;code&gt;varDecl&lt;/code&gt;, and hence a new environment &lt;code&gt;E0&lt;/code&gt; is created for &lt;code&gt;name&lt;/code&gt; to be stored in, with its value as &lt;code&gt;"World"&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;In Line 2, the parser sees a block through &lt;code&gt;{&lt;/code&gt;, due to which &lt;code&gt;visitBlockStmt()&lt;/code&gt; calls &lt;code&gt;executeBlock()&lt;/code&gt;, passing the list of statements in the block, while creating a new environment &lt;code&gt;E1&lt;/code&gt;, which has &lt;code&gt;E0&lt;/code&gt; as its parent.&lt;/li&gt;
&lt;li&gt;Again, &lt;code&gt;var&lt;/code&gt; in Line 3 (in the block), creates a new environment in &lt;code&gt;E1&lt;/code&gt;, with &lt;code&gt;greeting&lt;/code&gt; as the name and &lt;code&gt;"Hello, "&lt;/code&gt; as its value.&lt;/li&gt;
&lt;li&gt;When &lt;code&gt;print&lt;/code&gt; is encountered in Line 4, &lt;code&gt;evaluate(expr)&lt;/code&gt; is triggered, which finds that the print statement has a binary expression - &lt;code&gt;greeting + name&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;While Line 5 is being parsed, &lt;code&gt;visitAssignExpr()&lt;/code&gt; is called, which further calls &lt;code&gt;evaluate(expr)&lt;/code&gt;, after which the &lt;code&gt;Environment&lt;/code&gt; class' &lt;code&gt;assign()&lt;/code&gt; function is called. This checks for the presence of the variable (&lt;code&gt;name&lt;/code&gt;), first in its own environment &lt;code&gt;E1&lt;/code&gt;, and then recursively in parent environments until the &lt;code&gt;name&lt;/code&gt; is found.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;name&lt;/code&gt; is found in &lt;code&gt;E0&lt;/code&gt;, following which &lt;code&gt;put()&lt;/code&gt; updates its value in that environment: &lt;code&gt;"World" → "Lox"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Once the block ends in Line 6 (when &lt;code&gt;}&lt;/code&gt; is encountered), its environment &lt;code&gt;E1&lt;/code&gt; is destroyed.&lt;/li&gt;
&lt;li&gt;Finally, in Line 7, when &lt;code&gt;print name;&lt;/code&gt; is encountered, &lt;code&gt;name&lt;/code&gt;'s (changed) value &lt;code&gt;"Lox"&lt;/code&gt; is printed.
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy8fx533slu6ov0fnwdp3.png" alt=" " width="800" height="404"&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;What's next:&lt;/strong&gt; Support for logical operators, while, and for loops.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Musings:&lt;/strong&gt;&lt;br&gt;
I felt very, very, very happy when the code started working. Passing a .lox file felt almost magical. Except, I now know what went behind it. I still find myself most fascinated by the Scanner. Strings (and by extension, characters) never seemed very important to me, so I was pleasantly surprised to find out that they’re critical to interpreters/compilers. All it takes is a switch-case! I’m loving how much this project has taught me to appreciate all parts of programming.&lt;/p&gt;

</description>
      <category>computerscience</category>
      <category>compilers</category>
      <category>interpreter</category>
      <category>learning</category>
    </item>
    <item>
      <title>The Interpreter ✨</title>
      <dc:creator>Lahari Tenneti</dc:creator>
      <pubDate>Sat, 20 Sep 2025 19:22:32 +0000</pubDate>
      <link>https://dev.to/lahari_tenneti_4a8a082e9c/the-interpreter-1139</link>
      <guid>https://dev.to/lahari_tenneti_4a8a082e9c/the-interpreter-1139</guid>
      <description>&lt;p&gt;Finally, I created the Interpreter class that implements the visitor interface! From where all the function calls begin...&lt;br&gt;
We can now evaluate various kinds of expressions - Literal, Binary, Unary, and Grouping (wherein we use &lt;code&gt;accept()&lt;/code&gt; to recursively evaluate the subexpression within the &lt;code&gt;()&lt;/code&gt;).&lt;br&gt;
I also created the RuntimeError class to handle errors in Lox.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F56t3qd1oou8u5ssetfsv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F56t3qd1oou8u5ssetfsv.png" alt=" " width="800" height="569"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I built:&lt;/strong&gt; &lt;a href="https://github.com/laharitenneti/jlox-interpreter/commits/main/?since=2025-09-20&amp;amp;until=2025-09-20" rel="noopener noreferrer"&gt;Commits 6c14a8e &amp;amp; b49dfbc&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What I understood:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1) &lt;u&gt;The Interpreter class&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Like I'd previously mentioned, the visitor interface needs a separate class that implements it. This is how we "extend" behaviour.&lt;/li&gt;
&lt;li&gt;The class in question is &lt;code&gt;Interpret&lt;/code&gt;, that begins with the &lt;code&gt;interpret()&lt;/code&gt; method for evaluating and printing an expression, or reporting a runtime error.&lt;/li&gt;
&lt;li&gt;Also, all the &lt;code&gt;visitSubclass()&lt;/code&gt; methods we wrote in the &lt;code&gt;Expr&lt;/code&gt; class are overridden here to behave/act basis the type of expression parsed.&lt;/li&gt;
&lt;li&gt;These &lt;code&gt;visitSubclass()&lt;/code&gt; functions have not much to do - they merely extract the "values" from the "nodes" the Parser created and return them or the result of the expression they're in.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Override
    public Object visitBinaryExpr(Expr.Binary expr) {
        Object left = evaluate(expr.left);
        Object right = evaluate(expr.right);

        switch (expr.operator.type) {
            //comparison
            case GREATER:
                checkNumberOperands(expr.operator, left, right);
                return (double)left &amp;gt; (double)right;
            case GREATER_EQUAL:
                checkNumberOperands(expr.operator, left, right);
                return (double)left &amp;gt;= (double)right;
            case LESS:
                checkNumberOperands(expr.operator, left, right);
                return (double)left &amp;lt; (double)right;
            case LESS_EQUAL:
                checkNumberOperands(expr.operator, left, right);
                return (double)left &amp;lt;= (double)right;
            //arithmetic
            case MINUS:
                checkNumberOperands(expr.operator, left, right);
                return (double)left - (double)right;
            case PLUS:
                if (left instanceof Double &amp;amp;&amp;amp; right instanceof Double) {
                    return (double)left + (double)right;
                }
                if (left instanceof String &amp;amp;&amp;amp; right instanceof String) {
                    return (String)left + (String)right;
                }
                throw new RuntimeError(expr.operator, "Operands must be two numbers or two strings.");
            case STAR:
                checkNumberOperands(expr.operator, left, right);
                return (double)left * (double)right;
            case SLASH:
                checkNumberOperands(expr.operator, left, right);
                return (double)left / (double)right;
            //equality
            case BANG_EQUAL: return !isEqual(left, right);
            case EQUAL_EQUAL: return isEqual(left, right);
        }
        //Unreachable
        return null;
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;We also use helper functions like &lt;code&gt;checkNumberOperands()&lt;/code&gt;, &lt;code&gt;checkNumberOperand()&lt;/code&gt;, &lt;code&gt;isTruthy()&lt;/code&gt;, &lt;code&gt;isEqual()&lt;/code&gt;, and &lt;code&gt;evaluate()&lt;/code&gt; to simplify each &lt;code&gt;visitSubclass()&lt;/code&gt; function.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;2) &lt;u&gt;Runtime Errors&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Instead of having Lox's errors reported in Java, we needed to create a mechanism to display those errors in Lox itself. &lt;/li&gt;
&lt;li&gt;The underlying class we extend for this remains Java's &lt;code&gt;RuntimeException&lt;/code&gt; though.&lt;/li&gt;
&lt;li&gt;We create a &lt;code&gt;runtimeError()&lt;/code&gt; method in Lox that uses the &lt;code&gt;RuntimeError&lt;/code&gt; class we created, to print the error message and line number.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;What's next:&lt;/strong&gt; Evaluating and working with statements (and not just expressions)!&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Musings:&lt;/strong&gt;&lt;br&gt;
K-dramas are simply brilliant. I'm just using this platform to express my heartfelt gratitude for the gems that they are. And to the awesome people who make them. It's amusing how I'd scoff at their very mention just 3-4 years back. And I did so because I thought they were too unrealistic. Now I love them precisely because they're so unrealistic! I often look to them for inspiration on how not to take anything too seriously. When code or something else doesn't seem to work, I find it very helpful to watch yet another show revolving around a girl (a guy?) who suddenly finds herself in a Joseon-era royal kitchen. And lo! I return brimming with positivity, ideas, and the ability to understand just where in my code I erred. Sometimes, all we need to do is step back and take a chill-pill.&lt;/p&gt;

</description>
      <category>compilers</category>
      <category>interpreter</category>
      <category>computerscience</category>
      <category>learning</category>
    </item>
    <item>
      <title>The Parser!</title>
      <dc:creator>Lahari Tenneti</dc:creator>
      <pubDate>Thu, 18 Sep 2025 12:23:49 +0000</pubDate>
      <link>https://dev.to/lahari_tenneti_4a8a082e9c/day-4-the-parser-582l</link>
      <guid>https://dev.to/lahari_tenneti_4a8a082e9c/day-4-the-parser-582l</guid>
      <description>&lt;p&gt;Aaaand she's here! Last time, we created the rules/grammar basis which the parser converts tokens into syntax-tree. Now, we create association rules, i.e. we define the order in which expressions must be parsed to generate the tree.&lt;/p&gt;

&lt;p&gt;Recursive descent parsing is used here, wherein the parser starts from the outermost/top-level rule with lowest precedence and traverses through subexpressions to make it to the tree's bottom, where it finds literals (which happen to have the highest precedence).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2mtj1lverh6rq541edcq.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2mtj1lverh6rq541edcq.jpg" alt=" " width="800" height="1874"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I built:&lt;/strong&gt; &lt;a href="https://github.com/laharitenneti/jlox-interpreter/commit/5750470fe563e6b3b520b806dec4d534d9ca8849" rel="noopener noreferrer"&gt;Commit 5750470&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What I understood:&lt;/strong&gt;&lt;br&gt;
1) &lt;u&gt;Association Rules&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The rules we create must be defined as functions which call other functions of higher precedence, until we reach literals.&lt;/li&gt;
&lt;li&gt;We create a &lt;code&gt;Parser&lt;/code&gt; class with a list of tokens and a current pointer for noting the position.&lt;/li&gt;
&lt;li&gt;We also use a &lt;code&gt;match()&lt;/code&gt; function to look for certain tokens after the current one. This is the parser anticipating code based on the function it's currently in.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private boolean match(TokenType... types) {
     for (TokenType type : types) {
          if (check(type)) {
               advance();
               return true;
          }
     }
     return false;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;ParseError&lt;/code&gt; class (extending Java's &lt;code&gt;RuntimeError&lt;/code&gt; class) is created to handle errors.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;parse()&lt;/code&gt; is defined to call &lt;code&gt;expression()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;expression()&lt;/code&gt; calls &lt;code&gt;equality()&lt;/code&gt;, which calls &lt;code&gt;comparison()&lt;/code&gt;, which calls &lt;code&gt;term()&lt;/code&gt;, which calls &lt;code&gt;factor()&lt;/code&gt;, which further calls &lt;code&gt;unary()&lt;/code&gt;, which ultimately calls either itself or &lt;code&gt;primary()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;primary()&lt;/code&gt; checks for &lt;code&gt;LITERAL&lt;/code&gt;, &lt;code&gt;TRUE&lt;/code&gt;, &lt;code&gt;FALSE&lt;/code&gt;, and &lt;code&gt;LEFT-PAREN&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;When we encounter &lt;code&gt;LEFT-PAREN&lt;/code&gt;, we use &lt;code&gt;consume()&lt;/code&gt; to &lt;code&gt;advance()&lt;/code&gt; when the &lt;code&gt;RIGHT-PAREN&lt;/code&gt; is found. Else, a custom error is reported: &lt;em&gt;"Expect ')' after expression."&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;2) &lt;u&gt;Error Handling&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We use a method called 'Panic Mode Error Recovery,' which uses a concept called synchronisation to find the nearest acceptable token to restart parsing from, whenever the parser encounters an unanticipated/wrong token.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ex:&lt;/strong&gt; Tokens like &lt;code&gt;CLASS&lt;/code&gt;, &lt;code&gt;VAR&lt;/code&gt;, &lt;code&gt;FUN&lt;/code&gt;, &lt;code&gt;FOR&lt;/code&gt;, &lt;code&gt;IF&lt;/code&gt;, and &lt;code&gt;WHILE&lt;/code&gt; are good tokens to restart parsing from, because they mark the beginning of a new statement.&lt;/li&gt;
&lt;li&gt;This is the parser "panicking" and looking for a safe and graceful exit from the error line.&lt;/li&gt;
&lt;li&gt;It also prevents the parser from reporting a cascade of errors associated with the initial error.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ex:&lt;/strong&gt; If the parser sees:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var a = 1 + ;
var b = 2;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;It notes that after &lt;code&gt;+&lt;/code&gt;, we have a &lt;code&gt;;&lt;/code&gt;, when we should have another operand. This is an error.&lt;/li&gt;
&lt;li&gt;Without panic-mode error recovery, it would report a cascading errors like:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error on line 1: Expected an expression after '+'.
Error on line 2: Unexpected token 'Var'
Error on line 2: Missing expression.
Error on line 2: Unexpected semicolon.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;With panic-mode, &lt;code&gt;var&lt;/code&gt; in the second line, is according to &lt;code&gt;synchronisation()&lt;/code&gt;, a safe place to resume parsing from. So error reporting looks like:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error on line 1: Expected an expression after '+'.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;strong&gt;What I didn't understand:&lt;/strong&gt; How the hierarchy of association rules works. This is fundamentally based on the rules of precedence and association we define, and uses &lt;code&gt;match()&lt;/code&gt; to look for tokens, and then call the next appropriate rule (function).&lt;/p&gt;

&lt;p&gt;Let's say we have the expression: &lt;code&gt;5-3*2&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu2nuvnketvkft2r4ofw0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu2nuvnketvkft2r4ofw0.png" alt=" " width="800" height="700"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First, &lt;code&gt;parse()&lt;/code&gt; is called, which calls &lt;code&gt;expression()&lt;/code&gt;, which calls &lt;code&gt;equality()&lt;/code&gt;, and so on until we reach &lt;code&gt;primary()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Now, &lt;code&gt;primary()&lt;/code&gt; recognises that the first character &lt;code&gt;5&lt;/code&gt; is a &lt;code&gt;Literal&lt;/code&gt; and hence creates an object for it.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;primary()&lt;/code&gt; returns &lt;code&gt;Literal(5)&lt;/code&gt; to &lt;code&gt;unary()&lt;/code&gt;, which returns it to &lt;code&gt;factor()&lt;/code&gt;, which returns it to &lt;code&gt;term()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Now that the pointer is currently with the second character, &lt;code&gt;term()&lt;/code&gt; finds a match when it sees &lt;code&gt;-&lt;/code&gt;. Now that we have an operator, the right-operand must be found. &lt;/li&gt;
&lt;li&gt;Hence, &lt;code&gt;term()&lt;/code&gt; calls &lt;code&gt;factor()&lt;/code&gt; again, and so on, until &lt;code&gt;primary()&lt;/code&gt; finds and returns &lt;code&gt;3&lt;/code&gt; as another &lt;code&gt;Literal&lt;/code&gt; object.&lt;/li&gt;
&lt;li&gt;This object now reaches &lt;code&gt;factor()&lt;/code&gt;, which finds a match in &lt;code&gt;*&lt;/code&gt;, and decides that &lt;code&gt;3*&lt;/code&gt; needs a right-operand.&lt;/li&gt;
&lt;li&gt;Again, &lt;code&gt;factor()&lt;/code&gt; calls &lt;code&gt;unary()&lt;/code&gt;, which calls &lt;code&gt;primary()&lt;/code&gt;, which finds and returns &lt;code&gt;2&lt;/code&gt; as a &lt;code&gt;Literal&lt;/code&gt; object.&lt;/li&gt;
&lt;li&gt;This object reaches &lt;code&gt;factor()&lt;/code&gt;, which now has a full binary expression. It then creates a &lt;code&gt;Binary&lt;/code&gt; object like &lt;code&gt;Binary(3*2)&lt;/code&gt;, and returns it to &lt;code&gt;term()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Now, &lt;code&gt;term()&lt;/code&gt; has its right-operand for &lt;code&gt;5-&lt;/code&gt;, and finally constructs a full binary expression, i.e. &lt;code&gt;Binary(5-(3*2))&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;In this endeavour, by giving &lt;code&gt;factor()&lt;/code&gt; higher precedence over &lt;code&gt;term()&lt;/code&gt;, we ended up preserving the BODMAS rule.&lt;/li&gt;
&lt;li&gt;And lo! The binary expression is returned all the way to the top of the stack until &lt;code&gt;parse()&lt;/code&gt; returns it too. It is from this expression/object that we create a syntax tree.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq9axpo91sg5n3gd9okc1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq9axpo91sg5n3gd9okc1.png" alt=" " width="800" height="239"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What's next:&lt;/strong&gt; &lt;br&gt;
We’re going to evaluate the expressions we constructed the tree for! We're also going to extend the parser to handle not just expressions, but also statements - which is what actual code looks like.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Musings:&lt;/strong&gt;&lt;br&gt;
In my opinion, concepts are best understood through examples and metaphors. Unless we really engage with the subject at hand, we cannot internalise what's being taught. And we do this best by trying to use the pool of knowledge we have through our own experiences in life.&lt;br&gt;
I once read in the Upanishads that the best process to learn anything is by &lt;em&gt;shravana&lt;/em&gt; (listening to or reading from a credible guru or text), &lt;em&gt;manana&lt;/em&gt; (contemplating through deep thought, analysis, questioning, and even explaining it to another), and finally &lt;em&gt;nididhyasana&lt;/em&gt; (meditating upon the knowledge gained from the previous two stages, to transform it from a concept into a lived reality). &lt;br&gt;
While I’m not as thorough with this process as I’d like to be, I do try my best to understand a concept through various perspectives. It also gives me the opportunity to apply these learnt formulae to problems beyond computer-science. &lt;/p&gt;

</description>
      <category>compilers</category>
      <category>interpreter</category>
      <category>computerscience</category>
      <category>learning</category>
    </item>
    <item>
      <title>Grammar for the Parser</title>
      <dc:creator>Lahari Tenneti</dc:creator>
      <pubDate>Sun, 14 Sep 2025 19:07:21 +0000</pubDate>
      <link>https://dev.to/lahari_tenneti_4a8a082e9c/day-3-grammar-for-the-parser-2o88</link>
      <guid>https://dev.to/lahari_tenneti_4a8a082e9c/day-3-grammar-for-the-parser-2o88</guid>
      <description>&lt;p&gt;Well, it's been a while. Hyderabad's weather has been erratic lately and for a brief, brief time - so has my consistency towards this project. But I'm back!&lt;/p&gt;

&lt;p&gt;Anyhoo, the second stage of the Interpreter is the Parser, which accepts the tokens generated by the Scanner and creates a syntax-tree out of them. This tree is based on certain specified rules - the Context Free Grammar. &lt;/p&gt;

&lt;p&gt;As against regular language, which groups similar language into tokens but cannot adequately deal with deeply nested expressions, CFG allows for infinite possibilities of string/code (called derivations, because they’re “derived” from rules).&lt;/p&gt;

&lt;p&gt;But before we create the Parser itself, we need to create and define these rules, basis which trees can be constructed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr24trwkx2moiimxbnk40.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr24trwkx2moiimxbnk40.png" alt=" " width="800" height="553"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I built:&lt;/strong&gt; &lt;a href="https://github.com/laharitenneti/jlox-interpreter/commit/3264f2b05813cb73c4024a2d33a6528637275d9c" rel="noopener noreferrer"&gt;Commit 3264f2b&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What I understood:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1) &lt;u&gt;Introducing Trees and Grammar&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Trees are made of terminals and non-terminals (references to other rules).&lt;/li&gt;
&lt;li&gt;The Scanner emits Tokens which are consumed by the Parser, and grouped based on the grammar's rules, to produce nodes for the tree.&lt;/li&gt;
&lt;li&gt;Literals (string/numeric) are often leaf or end nodes, and operators - the internal nodes.&lt;/li&gt;
&lt;li&gt;Rules are also capable of referring to themselves, allowing for recursion and hence infinite possibilities.&lt;/li&gt;
&lt;li&gt;Based on Robert's &lt;a href="https://craftinginterpreters.com/representing-code.html#context-free-grammars" rel="noopener noreferrer"&gt;breakfast example&lt;/a&gt; to explain how rules are defined, I created my own example as well.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy9meeripnyffev1ze229.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy9meeripnyffev1ze229.png" alt=" " width="780" height="1024"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Terminals are enclosed in double-quotes and are in blue, while non-terminals are in black. Grouping/OR/repetitions are in purple. &lt;/li&gt;
&lt;li&gt;We can pick any of the options for a rule.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ex:&lt;/strong&gt; I choose the first rule in &lt;em&gt;chilling&lt;/em&gt;: &lt;code&gt;chilling → do activity;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;I now go to the non-terminal &lt;em&gt;do&lt;/em&gt; and choose any of its rules. Let's say: &lt;code&gt;do → "watch";&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Because &lt;em&gt;watch&lt;/em&gt; is a terminal, I have no other rule to refer to through it. So in &lt;em&gt;chilling&lt;/em&gt;, I replace &lt;em&gt;do&lt;/em&gt; with &lt;em&gt;watch&lt;/em&gt;, making it: &lt;code&gt;chilling → "watch" activity;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;I now go to the nonterminal &lt;em&gt;activity&lt;/em&gt; and choose a rule. &lt;/li&gt;
&lt;li&gt;I decide to pick &lt;em&gt;music&lt;/em&gt; (&lt;em&gt;movie&lt;/em&gt; would have been a more appropriate choice for "watch", but I want to demonstrate the flexibility CFG offers). &lt;code&gt;activity → music;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;In &lt;em&gt;music&lt;/em&gt;, which is a non-terminal, I choose: &lt;code&gt;music → "piano";&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Because there's nowhere to go beyond "piano," I have: &lt;code&gt;activity → "piano";&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Finally, going back to the first rule we started out with, we have a full-fledged instruction ready, based only on a few grammar rules: &lt;code&gt;chilling → "watch piano";&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;2) &lt;u&gt;Lox's actual grammar&lt;/u&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fphph2giztm3r2o06iezv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fphph2giztm3r2o06iezv.png" alt="Image" width="800" height="398"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We create a base class (or rule) called &lt;code&gt;Expr&lt;/code&gt; (expression), that refers to other subclasses (also rules), which can again refer to &lt;code&gt;Expr&lt;/code&gt;. This recursion is important because expressions generally consist of other expressions, which ultimately contain operators and operands.&lt;/li&gt;
&lt;li&gt;Because &lt;code&gt;Expr&lt;/code&gt; and its subclasses will have a similar, repetitive template, we metaprogram to have code write code for us. &lt;/li&gt;
&lt;li&gt;We do so by creating a general template that takes from a description and creates the relevant subclass using a for-loop.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;defineAst(outputDir, "Expr", Arrays.asList(
     "Binary: Expr left, Token operator, Expr right",
     "Grouping: Expr expression",
     "Literal: Object value",
     "Unary: Token operator, Expr right"
));

...

for (String type : types) {
     String className = type.split(":")[0].trim();
     String fields = type.split(":")[1].trim();
     defineType(writer, baseName, className, fields);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3) &lt;u&gt;The Expression Problem&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This is a huge issue programmers face when they wish to have multiple (sub)classes with similar behaviour (methods).&lt;/li&gt;
&lt;li&gt;In our situation, we deal with various subclasses like Binary, Unary, Literal, etc. There are some common methods necessary for them all. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ex:&lt;/strong&gt; Printing or computing the expression&lt;/li&gt;
&lt;li&gt;One way to deal with this would be by adding a print method for each subclass, i.e. &lt;code&gt;printBinary()&lt;/code&gt;, &lt;code&gt;printUnary()&lt;/code&gt;, &lt;code&gt;computeBinary()&lt;/code&gt;, &lt;code&gt;computeUnary()&lt;/code&gt;, etc.&lt;/li&gt;
&lt;li&gt;The issue with this approach is when we'd like to add a new method - say &lt;code&gt;typeCheck()&lt;/code&gt; - we'll have to update every subclass to implement its version of this new method, making the entire code bulky.&lt;/li&gt;
&lt;li&gt;Another way to resolve this is by using a common method that checks which subclass it must operate on, through if-else. But that is also problematic. What if we decide to add a new type/subclass? We'll have to update every (common) method to accommodate this new type. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;4) &lt;u&gt;Classes' Behaviour - What should it be like?&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;Expr&lt;/code&gt; class and its subclasses are merely data structures that must be passed between phases of the Interpreter.&lt;/li&gt;
&lt;li&gt;They must not have any "behaviour" (methods) of their own, lest we violate the 'Separation of Concerns' principle, which says that each part of the program must focus on one responsibility only. &lt;/li&gt;
&lt;li&gt;Because if we start mixing multiple responsibilities into one class, that class becomes harder to maintain, reuse, and extend without drastically altering it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;5) &lt;u&gt;Solution: The Visitor-Design Pattern&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This is a standard approach for solving the Expression Problem in object-oriented languages like Java.&lt;/li&gt;
&lt;li&gt;I see this as a bidirectional communication method, that overcomes the issues of the unidirectional approaches mentioned earlier.&lt;/li&gt;
&lt;li&gt;Here, we can extend the behaviour of (sub)classes while preserving their inherent structure, by simply creating a new visitor.&lt;/li&gt;
&lt;li&gt;We create a Visitor 'interface' with a list of &lt;code&gt;visitSubclass()&lt;/code&gt; methods, which aren't yet defined.&lt;/li&gt;
&lt;li&gt;The main/base class has an &lt;code&gt;accept()&lt;/code&gt; method that's extended and overridden by each of its subclasses.&lt;/li&gt;
&lt;li&gt;We then create a new class for a new behaviour. This class implements the Visitor interface, and is therefore the Visitor class.&lt;/li&gt;
&lt;li&gt;When I say "implements" the visitor interface, we just override the interface's undefined methods and give them specific behaviour.&lt;/li&gt;
&lt;li&gt;When this new class' object is created, it accepts an expression from the parser. This is the Visitor object.&lt;/li&gt;
&lt;li&gt;Depending on the expression's type (subclass), the &lt;code&gt;accept()&lt;/code&gt; method relevant to the type is called, with the class' object passed as an argument.&lt;/li&gt;
&lt;li&gt;The subclass' &lt;code&gt;accept()&lt;/code&gt; now receives the Visitor object and goes on to call the &lt;code&gt;visitSubclass()&lt;/code&gt; method associated with it, while also passing the subclass object as an argument. This is for giving visitSubclass() access to the subclass' fields.&lt;/li&gt;
&lt;li&gt;Remember when I said we overrode the interface's &lt;code&gt;visitSubclass()&lt;/code&gt; method in the new class and gave it specific behaviour? That behaviour (method) gets implemented here.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;What I didn't understand:&lt;/strong&gt; The Visitor-Design Pattern&lt;/p&gt;

&lt;p&gt;If the previous section completely went over your head, don't worry. It did for me too. I took two days to figure this out. 😁&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Let's understand this through an example. Assume that the behaviour we wish to implement here is of printing the expression. And assume that we need to print a binary expression.&lt;/li&gt;
&lt;li&gt;In our &lt;code&gt;Expr&lt;/code&gt; class, we create the Visitor interface, with methods like &lt;code&gt;visitBinary()&lt;/code&gt;, &lt;code&gt;visitUnary()&lt;/code&gt;, &lt;code&gt;visitLiteral()&lt;/code&gt;, &lt;code&gt;visitGrouping()&lt;/code&gt;, etc. Note that these methods are undefined here.&lt;/li&gt;
&lt;li&gt;We create an &lt;code&gt;accept()&lt;/code&gt; method for &lt;code&gt;Expr&lt;/code&gt; that is also undefined.&lt;/li&gt;
&lt;li&gt;In each subclass extending &lt;code&gt;Expr&lt;/code&gt;, we override &lt;code&gt;Expr&lt;/code&gt;’s &lt;code&gt;accept()&lt;/code&gt;, such that it calls the &lt;code&gt;visitSubclass()&lt;/code&gt; method associated with it. For example, the &lt;code&gt;Binary&lt;/code&gt; subclass’s &lt;code&gt;accept()&lt;/code&gt; will call &lt;code&gt;visitBinaryExpr(this)&lt;/code&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;abstract class Expr {
  abstract &amp;lt;R&amp;gt; R accept(Visitor&amp;lt;R&amp;gt; visitor);

  interface Visitor&amp;lt;R&amp;gt; {
    R visitBinaryExpr(Binary expr);
    R visitGroupingExpr(Grouping expr);
    R visitLiteralExpr(Literal expr);
    R visitUnaryExpr(Unary expr);
  }

  static class Binary extends Expr {
    Binary(Expr left, Token operator, Expr right) {
      this.left = left;
      this.operator = operator;
      this.right = right;
    }
    @Override
    &amp;lt;R&amp;gt; R accept(Visitor&amp;lt;R&amp;gt; visitor) {
      return visitor.visitBinaryExpr(this);
    }
    final Expr left;
    final Token operator;
    final Expr right;
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;this&lt;/code&gt; is passed as an argument because the subclass is giving the visitor object full access to itself, and therefore all its fields.&lt;/li&gt;
&lt;li&gt;We now create a new class called &lt;code&gt;PrintExpr&lt;/code&gt; that implements &lt;code&gt;Expr&lt;/code&gt;'s Visitor interface.&lt;/li&gt;
&lt;li&gt;It then overrides all the &lt;code&gt;visitSubclass()&lt;/code&gt; methods to print the expression associated with each subclass.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;visitBinaryExpr()&lt;/code&gt; will be overridden to return/print the whole binary expression.&lt;/li&gt;
&lt;li&gt;We can create as many behaviours as we like without changing a single thing about the subclasses.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class PrintExpr implements Expr.Visitor&amp;lt;String&amp;gt; {
    String print(Expr expr) {
        return expr.accept(this);
    }

    @Override
    public String visitBinaryExpr(Expr.Binary expr) {
        return expr.left.accept(this) + " " + expr.operator.lexeme + " " + expr.right.accept(this);
    }

    public static void main(String[] args) {
        Expr expression = new Expr.Binary(
            new Expr.Unary(
                new Token(TokenType.MINUS, "-", null, 1),
                new Expr.Literal(123)
            ),
            new Token(TokenType.STAR, "*", null, 1),
            new Expr.Grouping(
                new Expr.Literal(45.67)
            )
        );

        System.out.println(new PrintExpr().print(expression));
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7oslize762lggzyqm115.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7oslize762lggzyqm115.png" alt=" " width="800" height="47"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;If I had to explain it in a very crude way:&lt;/strong&gt; Let's say there's a census being conducted, for which an officer must visit your house. They are supposed to count members of your house, and cannot visit without an invitation. Maybe they'll also issue certain ID cards. (Both these - counting and issuing cards - are behaviours)&lt;/li&gt;
&lt;li&gt;The Census Office creates an interface that sends out mails informing families of the census. (This is the Visitor Interface and families/houses are subclasses)&lt;/li&gt;
&lt;li&gt;When this mail is received by a house (liken this to the visitor object passing itself), it is supposed to accept the visitor (&lt;code&gt;accept()&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;The house is also supposed to send a mail back to the Census office saying "Yes, we agree to allow the officer inside." (This is the subclass giving the visitor object access to itself through &lt;code&gt;this&lt;/code&gt; while calling &lt;code&gt;visitSubclass()&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;The officer then arrives and either counts or issues an ID card (depending on the class we've created to implement the behaviour).&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;What's next:&lt;/strong&gt; For real, the Parser. Like "Parser" Parser. Proper Parser.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Musings:&lt;/strong&gt;&lt;br&gt;
Nothing interpreter related, honestly. But I had a really good time today. Spent about 15-20 minutes in the balcony looking at the city. I really, really love balconies. There's no such thing as a bad balcony, I guess? &lt;br&gt;
You see, they show us a world beyond the comfort of wherever we are. They offer us a phenomenal vantage point from where we can watch countless little dramas unfold; from where we can both appreciate man's incredible skill in creating these chaotically beautiful structures, and also find ourselves amused at how it's the lone, calm green tree breaking through the maze of streets, that we find ourselves most in awe of.&lt;/p&gt;

</description>
      <category>computerscience</category>
      <category>compilers</category>
      <category>interpreter</category>
      <category>learning</category>
    </item>
  </channel>
</rss>
