<?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 #4 — User Defined Operators</title>
      <dc:creator>Lahari Tenneti</dc:creator>
      <pubDate>Wed, 13 May 2026 11:56:31 +0000</pubDate>
      <link>https://dev.to/lahari_tenneti_4a8a082e9c/llvm-4-user-defined-operators-3f58</link>
      <guid>https://dev.to/lahari_tenneti_4a8a082e9c/llvm-4-user-defined-operators-3f58</guid>
      <description>&lt;p&gt;Kaleidoscope's grammar can now be extended by the user. They can define their own binary and unary operators with custom symbols and precedence, without rewriting the parser or adding new cases to the codegen.&lt;/p&gt;

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




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

&lt;p&gt;The idea is simple. We should allow for users to write something like:&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%2Fxplc91k6k8g5oiqukzth.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%2Fxplc91k6k8g5oiqukzth.png" alt=" " width="786" height="344"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;and&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%2Fr765qe3v9tg1lamp9c56.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%2Fr765qe3v9tg1lamp9c56.png" alt=" " width="800" height="260"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Through which we can do:&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%2F0r4vfcwqd4xy8o13nnzh.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%2F0r4vfcwqd4xy8o13nnzh.png" alt=" " width="800" height="778"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;We add two new tokens: &lt;code&gt;tok_binary&lt;/code&gt; and &lt;code&gt;tok_unary&lt;/code&gt;, along with their checks in &lt;code&gt;gettok()&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;We create a &lt;code&gt;UnaryExprAST&lt;/code&gt;, which is pretty similar to &lt;code&gt;BinaryExprAST&lt;/code&gt;, but with one child instead of two. &lt;/li&gt;
&lt;li&gt;We also extend &lt;code&gt;PrototypeAST&lt;/code&gt; to have two new fields: &lt;code&gt;IsOperator&lt;/code&gt; (&lt;code&gt;bool&lt;/code&gt;) and &lt;code&gt;Precedence&lt;/code&gt; (&lt;code&gt;unsigned&lt;/code&gt;). &lt;/li&gt;
&lt;li&gt;A prototype now knows whether it's defining an operator, and if yes, at what precedence.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ParsePrototype()&lt;/code&gt; uses a switch-case on &lt;code&gt;CurTok&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If it sees &lt;code&gt;tok_identifier&lt;/code&gt;, it's a regular function, like before.&lt;/li&gt;
&lt;li&gt;But if it sees &lt;code&gt;tok_binary&lt;/code&gt;, it reads the operator character, optionally reads a precedence number (in case of binary), and builds the name &lt;code&gt;"binary" + char&lt;/code&gt; (so &lt;code&gt;binary|&lt;/code&gt;, &lt;code&gt;binary&amp;gt;&lt;/code&gt;, etc.).&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If it sees &lt;code&gt;tok_unary&lt;/code&gt;, it does the same but without precedence.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;ParseUnary()&lt;/code&gt; is also new. It sits between &lt;code&gt;ParseExpression()&lt;/code&gt; and &lt;code&gt;ParsePrimary()&lt;/code&gt; in the call chain. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If the current token looks like a unary operator (an ASCII character that isn't &lt;code&gt;(&lt;/code&gt; or &lt;code&gt;,&lt;/code&gt;), it consumes it and recursively calls &lt;code&gt;ParseUnary()&lt;/code&gt; on the rest. This is for handling chaining (like &lt;code&gt;!!x&lt;/code&gt;). Otherwise, it falls through to &lt;code&gt;ParsePrimary()&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;ParseExpression()&lt;/code&gt; and &lt;code&gt;ParseBinOpRHS()&lt;/code&gt; are also updated to call &lt;code&gt;ParseUnary()&lt;/code&gt; instead of &lt;code&gt;ParsePrimary()&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;This is where things change a bit.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For binary operators, &lt;code&gt;BinaryExprAST::codegen()&lt;/code&gt; already had a switch-case on &lt;code&gt;Op&lt;/code&gt;. We just add a default case that does a symbol table lookup for &lt;code&gt;"binary" + Op&lt;/code&gt; and emits a call to it:
&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="n"&gt;F&lt;/span&gt; &lt;span class="o"&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;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"binary"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;Op&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;F&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="s"&gt;"binary operator not found!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Ops&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&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="n"&gt;L&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;R&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;Builder&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;CreateCall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Ops&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"binop"&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;User-defined operators are mostly similar to functions (only with new names). The codegen doesn't bother distinguishing whether it's a function or UDF; It merely finds the function and calls it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Likewise, for unary operators, &lt;code&gt;UnaryExprAST::codegen()&lt;/code&gt; looks up &lt;code&gt;"unary" + Opcode&lt;/code&gt; and calls it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Like I mentioned earlier, before building the function body, if the prototype is a binary operator, we register its precedence in &lt;code&gt;BinopPrecedence&lt;/code&gt;. This change is made in &lt;code&gt;FunctionAST::codegen()&lt;/code&gt;:&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;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;P&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isBinaryOp&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="n"&gt;BinopPrecedence&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;P&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getOperatorName&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;P&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getBinaryPrecedence&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 grammar is dynamically extensible at JIT runtime: define a new operator and it is immediately available with the right precedence.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;What I didn't understand:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;a) How does &lt;u&gt;naming operators&lt;/u&gt; &lt;code&gt;binary|&lt;/code&gt; or &lt;code&gt;unary!&lt;/code&gt; work?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I had this question because we construct a string like &lt;code&gt;binary|&lt;/code&gt; and use it as a function name. &lt;/li&gt;
&lt;li&gt;The thing is, LLVM's symbol table allows names with symbols, so &lt;code&gt;binary|&lt;/code&gt; is a perfectly valid function name in LLVM IR. &lt;/li&gt;
&lt;li&gt;When the user writes &lt;code&gt;x | y&lt;/code&gt;, codegen looks up &lt;code&gt;binary|&lt;/code&gt; in the module, finds the user-defined function, and emits a call. &lt;/li&gt;
&lt;li&gt;It is an ordinary function dispatch dressed up to look like operator syntax.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;b) Why do user-defined operators not need &lt;u&gt;new AST nodes?&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This is because the existing &lt;code&gt;BinaryExprAST&lt;/code&gt; and &lt;code&gt;UnaryExprAST&lt;/code&gt; already represent “an operator applied to operands”, and thus don't care whether the operator is built-in or user-defined. &lt;/li&gt;
&lt;li&gt;The only thing that changes is what &lt;code&gt;codegen()&lt;/code&gt; does with an unrecognised &lt;code&gt;Op&lt;/code&gt;. Instead of erroring, it looks the operator up as a function. &lt;/li&gt;
&lt;li&gt;The AST stays blissfully unaware of the distinction.&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%2F9lsgc4ssvy2ovzgm576o.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%2F9lsgc4ssvy2ovzgm576o.png" alt=" " width="800" height="678"&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%2Fqsk0uvmlvi3mpmr0gf0k.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%2Fqsk0uvmlvi3mpmr0gf0k.png" alt=" " width="800" height="834"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What's next:&lt;/strong&gt; Mutable variables and SSA construction (the last big piece).&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Musings:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I have the insanest Tiny Chef obsession. He's the most adorable, sassy, and tiny little bundle of joy I've seen in a long, long time now. He's so refreshingly and unabashedly authentic. Bad singing (but still does it anyway), pop-astrology, yoga, wardrobe dilemmas, and above all — unapologetic optimism. I never imagined I'd find myself rooting for, or seeking life-lessons from a barely legible green little ball of felt. But hey, here we are. When the going gets tough, all we've got to do is put our hand on our heart and say, "You know what? I'm blenough, and it's all going to be blokay." &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%2Fpn4h3tetl9flrlllbuf3.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%2Fpn4h3tetl9flrlllbuf3.png" alt=" " width="800" height="620"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>compilers</category>
      <category>llvm</category>
      <category>computerscience</category>
      <category>learning</category>
    </item>
    <item>
      <title>LLVM #3 — Control Flow</title>
      <dc:creator>Lahari Tenneti</dc:creator>
      <pubDate>Wed, 06 May 2026 10:16:56 +0000</pubDate>
      <link>https://dev.to/lahari_tenneti_4a8a082e9c/llvm-3-control-flow-1c42</link>
      <guid>https://dev.to/lahari_tenneti_4a8a082e9c/llvm-3-control-flow-1c42</guid>
      <description>&lt;p&gt;After all we've done (building a lexer, parser, code-generator, optimiser, and the JIT), we give Kaleidoscope decision-making abilities by adding support for if/then else conditionals and for-loops.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I built:&lt;/strong&gt; &lt;a href="https://github.com/laharitenneti/Kaleidoscope/commit/5ba58038714185be6bd1471d9719d2539f03fa07" rel="noopener noreferrer"&gt;Commit 5ba5803&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;u&gt;If/Then/Else:&lt;/u&gt;&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;We add three new tokens, namely &lt;code&gt;tok_if&lt;/code&gt;, &lt;code&gt;tok_then&lt;/code&gt;, and &lt;code&gt;tok_else&lt;/code&gt;, and their corresponding checks in &lt;code&gt;gettok()&lt;/code&gt; (through &lt;code&gt;if (IdentifierStr == ...)&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;The &lt;code&gt;IfExprAST&lt;/code&gt; holds three child expressions — &lt;code&gt;Cond&lt;/code&gt;, &lt;code&gt;Then&lt;/code&gt;, and &lt;code&gt;Else&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;It's worth noting that in Kaleidoscope, everything is an expression. There are no statements. This means that &lt;code&gt;if/then/else&lt;/code&gt; doesn't result in an action, and instead returns a value.&lt;/li&gt;
&lt;li&gt;This is to keep the language consistent, as the codegen never has to resolve whether something is an expression or a statement, as only the former is allowed.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;ParseIfExpr()&lt;/code&gt; consumes &lt;code&gt;if&lt;/code&gt;, parses the condition, expects &lt;code&gt;then&lt;/code&gt;, parses the then-expression, expects &lt;code&gt;else&lt;/code&gt;, parses the else-expression, and returns an &lt;code&gt;IfExprAST&lt;/code&gt;. This is simple recursive descent at play.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;When we generate code for an if-then-else condition, we can't emit instructions linearly anymore. The two branches (then, else) are mutually exclusive, and only one runs. &lt;/li&gt;
&lt;li&gt;This is where we need proper control-flow: a conditional branch, two separate blocks of code, and a merge point.&lt;/li&gt;
&lt;li&gt;That looks like:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight llvm"&gt;&lt;code&gt;&lt;span class="nl"&gt;entry:&lt;/span&gt;
   &lt;span class="nv"&gt;%ifcond&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;fcmp&lt;/span&gt; &lt;span class="k"&gt;one&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;0.0&lt;/span&gt;
   &lt;span class="k"&gt;br&lt;/span&gt; &lt;span class="kt"&gt;i1&lt;/span&gt; &lt;span class="nv"&gt;%ifcond&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;label&lt;/span&gt; &lt;span class="nv"&gt;%then&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;label&lt;/span&gt; &lt;span class="nv"&gt;%else&lt;/span&gt;

&lt;span class="nl"&gt;then:&lt;/span&gt;
   &lt;span class="nv"&gt;%calltmp&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;call&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="vg"&gt;@foo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
   &lt;span class="k"&gt;br&lt;/span&gt; &lt;span class="kt"&gt;label&lt;/span&gt; &lt;span class="nv"&gt;%ifcont&lt;/span&gt;

&lt;span class="nl"&gt;else:&lt;/span&gt;
  &lt;span class="nv"&gt;%calltmp1&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;call&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="vg"&gt;@bar&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;br&lt;/span&gt; &lt;span class="kt"&gt;label&lt;/span&gt; &lt;span class="nv"&gt;%ifcont&lt;/span&gt;

&lt;span class="nl"&gt;ifcont:&lt;/span&gt;
  &lt;span class="nv"&gt;%iftmp&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;phi&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;%calltmp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;%then&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;%calltmp1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;%else&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;ret&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="nv"&gt;%iftmp&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What I didn't understand (in if/else):&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;a) &lt;u&gt;Why basic blocks?&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code is merely a list of instructions. Why not just emit them line-by-line and tell the CPU to 'jump' when it hits an &lt;code&gt;if&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;This is because it would be nightmarish for the optimiser to understand the flow in such a scenario. LLVM hence forces us to use 'basic blocks,' which are chunks of code guaranteed to execute from beginning to end, without any jumping in or out.&lt;/li&gt;
&lt;li&gt;Through blocks, the compiler has a somewhat high-level map (&lt;strong&gt;Ex:&lt;/strong&gt; It knows exactly what happens in a &lt;code&gt;ThenBB&lt;/code&gt;, an &lt;code&gt;ElseBB&lt;/code&gt;, etc.), which matters for optimisation. If the optimiser knows a block runs as a unit, it can reason about the whole block at once.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;b) &lt;u&gt;Why the Phi node?&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why couldn't I just assign a value to a variable in both blocks, depending on the condition, and then have the &lt;code&gt;ifcont&lt;/code&gt; read the value? For example:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;
&lt;span class="n"&gt;ans&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;
&lt;span class="nf"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="n"&gt;ans&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;even&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;
   &lt;span class="n"&gt;ans&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;odd&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;This is because LLVM uses SSA. The same variable cannot be changed once defined, thereby making the &lt;code&gt;else&lt;/code&gt; branch impossible unless... we use a Phi node.&lt;/li&gt;
&lt;li&gt;This Phi node sits at the junction where both &lt;code&gt;if&lt;/code&gt; and &lt;code&gt;else&lt;/code&gt; branches meet. It does no "calculation" and only looks back at the path the CPU took. At runtime, when the CPU jumps from &lt;code&gt;then&lt;/code&gt; or &lt;code&gt;else&lt;/code&gt; to &lt;code&gt;ifcont&lt;/code&gt;, it already carries information about which block it just came from. &lt;/li&gt;
&lt;li&gt;Phi reads that "came from" information and resolves to the corresponding value.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ex:&lt;/strong&gt; my value is &lt;code&gt;calltmp&lt;/code&gt; if we came from &lt;code&gt;then&lt;/code&gt;, or &lt;code&gt;calltmp1&lt;/code&gt; if we came from &lt;code&gt;else&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;c) &lt;u&gt;Why do we insert the Phi node manually&lt;/u&gt; for &lt;code&gt;if/else&lt;/code&gt;, instead of using &lt;code&gt;alloca&lt;/code&gt; + &lt;code&gt;mem2reg&lt;/code&gt; to handle user variables?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This is because we know exactly where the merge happens. &lt;/li&gt;
&lt;li&gt;When codegen is processing an &lt;code&gt;IfExprAST&lt;/code&gt;, it knows the shape of the problem before it even starts. There are two branches. They will meet at exactly one point. That meeting point needs exactly one Phi node with exactly two inputs. It's the same every single time, no matter what. So we just write it directly. &lt;/li&gt;
&lt;li&gt;Contrarily, &lt;code&gt;alloca&lt;/code&gt; + &lt;code&gt;mem2reg&lt;/code&gt; is more helpful when code looks like this:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&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;Here, &lt;code&gt;x&lt;/code&gt; gets assigned in multiple places. And in a more complex program, those assignments could be scattered across loops, nested ifs, all over the place. The compiler can't just look at the AST node for x and know where to put the Phi. It would have to trace every possible path through the entire program to figure out where values of &lt;code&gt;x&lt;/code&gt; merge. &lt;/li&gt;
&lt;li&gt;So instead of doing that hard work ourselves, we use &lt;code&gt;alloca&lt;/code&gt; (we give &lt;code&gt;x&lt;/code&gt; a slot in memory, let every branch just write to that slot, and then hand it off to &lt;code&gt;mem2reg&lt;/code&gt;). &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;mem2reg&lt;/code&gt; is a pass that already knows how to trace control flow, find all the merge points, and insert the right Phi nodes automatically.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;d) &lt;u&gt;Why do we re-fetch the &lt;code&gt;ThenBB&lt;/code&gt; block?&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why is there a need for another &lt;code&gt;ThenBB = Builder -&amp;gt; GetInsertBlock()&lt;/code&gt; if we already had it?&lt;/li&gt;
&lt;li&gt;This is to account for recursion. If the code inside our &lt;code&gt;then&lt;/code&gt; block is a simple &lt;code&gt;x + y&lt;/code&gt;, the pointer is the same. But if it contains another &lt;code&gt;if/else&lt;/code&gt;, the nested &lt;code&gt;if&lt;/code&gt; will create its own blocks and move the builder's insertion point. &lt;/li&gt;
&lt;li&gt;In this case, the builder sits at the end of the nested merge block. Hence, we re-fetch it because the Phi node needs to know the final block that ran, and not necessarily the one we started out with.&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%2F4lfeb6zezj7jtqv3upjf.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%2F4lfeb6zezj7jtqv3upjf.png" alt=" " width="800" height="875"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;u&gt;The for loop:&lt;/u&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
  &lt;span class="n"&gt;putchard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;42&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;There are four parts to this: start value, end condition, step value (defaults to 1.0), and the body. We follow the same 'lexer, parser, AST, codegen' template as &lt;code&gt;if/else&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The IR generated by the codegen looks like:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight llvm"&gt;&lt;code&gt;&lt;span class="nl"&gt;entry:&lt;/span&gt;
  &lt;span class="k"&gt;br&lt;/span&gt; &lt;span class="kt"&gt;label&lt;/span&gt; &lt;span class="nv"&gt;%loop&lt;/span&gt;

&lt;span class="nl"&gt;loop:&lt;/span&gt;
  &lt;span class="nv"&gt;%i&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;phi&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="m"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;%entry&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;%nextvar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;%loop&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nv"&gt;%calltmp&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;call&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="vg"&gt;@putchard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="m"&gt;42.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nv"&gt;%nextvar&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;%i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1.0&lt;/span&gt;
  &lt;span class="nv"&gt;%loopcond&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;fcmp&lt;/span&gt; &lt;span class="k"&gt;one&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="nv"&gt;%booltmp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.0&lt;/span&gt;
  &lt;span class="k"&gt;br&lt;/span&gt; &lt;span class="kt"&gt;i1&lt;/span&gt; &lt;span class="nv"&gt;%loopcond&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;label&lt;/span&gt; &lt;span class="nv"&gt;%loop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;label&lt;/span&gt; &lt;span class="nv"&gt;%afterloop&lt;/span&gt;

&lt;span class="nl"&gt;afterloop:&lt;/span&gt;
  &lt;span class="k"&gt;ret&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="m"&gt;0.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What I didn't understand (in for loops):&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;a) &lt;u&gt;Why is there a preheader&lt;/u&gt; in the for-loop, before it even starts?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Again, for the Phi node. It needs to know which block a value came from. When we enter a loop for the first time, we aren't wntering from within the loop itself. As trivial as it sounds, we enter from outside the loop.&lt;/li&gt;
&lt;li&gt;The Preheader thus gives the Phi node a clear starting point for the first iteration, and without which the Phi node wouldn't know what our counter's initial value should be.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;b) &lt;u&gt;Why return a &lt;code&gt;0.0&lt;/code&gt;?&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;As of now, our for loops return a value of &lt;code&gt;0.0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;This is because we don't have mutable memory yet. The loop variable disappears once the loop ends, causing the body's value to not be accumulated anywhere. &lt;/li&gt;
&lt;li&gt;This is just a temporary placeholder of sorts.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;c) &lt;u&gt;What if the variable we use for the loop already exists?&lt;/u&gt; What happens to its value after the loop ends?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This is better understood with an example:
&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;var&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;99&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt;
  &lt;span class="nf"&gt;putchard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&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 outer &lt;code&gt;i&lt;/code&gt;'s value is 99. And the loop has an &lt;code&gt;i&lt;/code&gt; value of its own. &lt;/li&gt;
&lt;li&gt;The problem is that both live in the same symbol table, &lt;code&gt;NamedValues&lt;/code&gt;. Whenever the loop writes &lt;code&gt;NamedValues["i"] = Variable&lt;/code&gt;, the previous value gets overwritten. Thus, when the loop ends, the pre-loop value is either gone, or replaced by the loop's last value. &lt;/li&gt;
&lt;li&gt;The answer to this is Variable shadowing. Pretty similar to what we did with Lox's environments. We peek at &lt;code&gt;NamedValues&lt;/code&gt; to see that pre-loop value, and save it.
&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;Value&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;OldVal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;NamedValues&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;VarName&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="n"&gt;NamedValues&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;VarName&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Variable&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// loop's i takes over&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we restore:&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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OldVal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;NamedValues&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;VarName&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;OldVal&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// 99 comes back&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;
  &lt;span class="n"&gt;NamedValues&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;erase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;VarName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;     &lt;span class="c1"&gt;// nothing was there before, so clean up&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The inner variable doesn't destroy the outer one and only temporarily steps in front of it. Once the loop exits, the outer scope is exactly as it was. Same principle as Lox's chained environments, just done manually here since we're managing the symbol table ourselves.&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%2F48b7f6mtfe90cesad9le.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%2F48b7f6mtfe90cesad9le.png" alt=" " width="800" height="608"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What's next:&lt;/strong&gt; Extending Kaleidoscope with user-defined operators. More control.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Musings:&lt;/strong&gt;&lt;br&gt;
Hehe, hello again. Twenty thousand different things came up. I hope to be more consistent with this though. I mean, it is sort of good that I keep coming back. Not even as an obligation because I actually enjoy it very much. But I could do better.&lt;/p&gt;

&lt;p&gt;Anyhoo, I've been walking quite a bit lately (which I absolutely love). It is among the few things I do to relax. I think excellently when I'm walking. Any problem I feel I'm unable to find a solution to seems to solve itself merely 15-20 minutes into a walk. I feel more optimistic and in charge of life. I also learn to, briefly though it may be, set aside all that goes on in my little world, and just feel like I'm part of something bigger. Almost akin to that mix of awe and ease one feels knowing they're in the audience witnessing something grand. Our world too, can be beautiful and inspire hope if we figure out how to look at it. A walk out there helps. &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%2Fj0lqo0wjktc7gsvg3igb.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%2Fj0lqo0wjktc7gsvg3igb.png" alt=" " width="800" height="681"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>llvm</category>
      <category>compilers</category>
      <category>computerscience</category>
      <category>learning</category>
    </item>
    <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>
  </channel>
</rss>
