<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Vitor&apos;s thoughts</title><description>thoughts, PL, CS and math</description><link>https://vitorsalmeida.com/</link><item><title>Is Data Struct about memory?</title><link>https://vitorsalmeida.com/is-data-struct-about-memory/</link><guid isPermaLink="true">https://vitorsalmeida.com/is-data-struct-about-memory/</guid><pubDate>Sun, 27 Aug 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;A principle that I try to follow is to think about obvious concepts. Since we are introduced to society, we meet with a lot of concepts that we don&apos;t even question. Some authoritary figure like a teacher or a parent tells us things that we just accept.&lt;/p&gt;
&lt;p&gt;If you think about data structure, you can relate it immediately to memory. Today, I want to discuss why data structures &lt;strong&gt;are not only&lt;/strong&gt; about memory.&lt;/p&gt;
&lt;h3&gt;Introduction&lt;/h3&gt;
&lt;p&gt;For instance, let’s understand that we do not have any problem thinking about data structures to organize data in memory. The problem is never the idea that data structures are indifferent to implementation.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Data_structure#Language_support&quot;&gt;Data Structure&lt;/a&gt; is a way to organize, manage, and store data (yes, you can store things in math. If you think about &lt;a href=&quot;https://en.wikipedia.org/wiki/Matrix_(mathematics)&quot;&gt;matrices&lt;/a&gt; or &lt;a href=&quot;https://en.wikipedia.org/wiki/Set_(mathematics)&quot;&gt;sets&lt;/a&gt;, you can notice that is a way to organize data. In practice, store things is about to organize things). It is a collection of values that we can realize operations. In essence, it is an &lt;strong&gt;Algebraic Structure&lt;/strong&gt; of data.&lt;/p&gt;
&lt;h3&gt;Algebraic Structure&lt;/h3&gt;
&lt;p&gt;An algebraic Structure is a set of elements with one or more operations that satisfy some specific properties (axioms). An example is Boolean Algebra, a set with two binary operations, union and intersection. You can follow this link to understand more about &lt;a href=&quot;https://en.wikipedia.org/wiki/Boolean_algebra#Boolean_algebras&quot;&gt;Boolean Algebra&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;At this point, I want to highlight that algebraic structures are essentially about math. It describes the properties of a set of elements: a &lt;a href=&quot;https://en.wikipedia.org/wiki/Group_(mathematics)&quot;&gt;group&lt;/a&gt;, a &lt;a href=&quot;https://en.wikipedia.org/wiki/Ring_(mathematics)&quot;&gt;ring&lt;/a&gt;, an &lt;a href=&quot;https://en.wikipedia.org/wiki/Algebra_over_a_field&quot;&gt;algebra&lt;/a&gt;, etc. If you want to dive into this topic, read &lt;a href=&quot;https://en.wikipedia.org/wiki/Algebraic_structure&quot;&gt;Algebraic Structures&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It is a big mistake to take an abstract and mathematical concept and reduce it to a simple implementation that needs physical and limited resources. You do not need any computer or memory to understand or use data structures. You need &lt;a href=&quot;https://en.wikipedia.org/wiki/Data&quot;&gt;data&lt;/a&gt;. And data in math is just a set of elements.&lt;/p&gt;
&lt;h3&gt;Let&apos;s to think about computational models&lt;/h3&gt;
&lt;p&gt;The root of this problem is that you maybe don&apos;t know about computational models. It is a important step because if you think that in computer science we are using math to model computational problems, and computational problems are mathematical problems, you probably never will reduce any computational problem to a phisical and limited resource (like memory).&lt;/p&gt;
&lt;p&gt;Computer science problems follow math problems, and we use math to model them. It means that the problem that you&apos;re trying to solve, can be modeled by a mathematical problem before you start to think about some computational implementation.&lt;/p&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;I hope that you understand that data structures &lt;strong&gt;are not only&lt;/strong&gt; about memory. It is a mathematical concept that we use to model computational problems.&lt;/p&gt;
&lt;p&gt;This text is a little talk about some ideas that I would like to share. I tried to give some references to help if you want to dive into this topic. Also, it&apos;s &lt;strong&gt;very&lt;/strong&gt; important to think about obvious concepts and try to understand &lt;strong&gt;why&lt;/strong&gt; the things are the way they are.&lt;/p&gt;
</content:encoded></item><item><title>Introduction to Big O notation</title><link>https://vitorsalmeida.com/intro-bigo/</link><guid isPermaLink="true">https://vitorsalmeida.com/intro-bigo/</guid><pubDate>Sat, 12 Aug 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h3&gt;Introduction&lt;/h3&gt;
&lt;p&gt;In this post, we will explore the concept of algorithm efficiency and how to measure this efficiency using big O notation. Additionally, we will see how this can help us write more performance code. Big O notation allows us to evaluate the performance of an algorithm according to the size of its data set.&lt;/p&gt;
&lt;h3&gt;What is big O notation and algorithm efficiency?&lt;/h3&gt;
&lt;p&gt;Algorithm efficiency is the ability to solve a problem in a reasonable time and with efficient use of computational resources such as processing and memory. Big O notation is a way of measuring the efficiency of an algorithm, describing the asymptotic behavior of a function. This means that we can evaluate the rate of growth of the function, comparing it to other algorithms.&lt;/p&gt;
&lt;p&gt;Big O notation is represented by the letter O and is used as follows: Θ(f(n)), where f(n) is the measure by which the size of the input (n) increases. For example, an algorithm that grows quadratically, that is, increases proportionally to the square of each additional input, is represented by Θ(n²).&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Constant time: Θ(1)&lt;/li&gt;
&lt;li&gt;Linear time: Θ(n)&lt;/li&gt;
&lt;li&gt;Logarithmic time: Θ(log(n))&lt;/li&gt;
&lt;li&gt;Quadratic time: Θ(n²)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Big O notation in code&lt;/h3&gt;
&lt;p&gt;Suppose you have a list and need to find a certain element &apos;x&apos;. This algorithm is called sequential search.&lt;/p&gt;
&lt;p&gt;Another example is when you need to sort a list. This algorithm is called insertion sort.&lt;/p&gt;
&lt;p&gt;Sequential search example&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// linear time
function sequentialSearch(arr: number[], x: number) {
  for (let i = 0; i &amp;lt; arr.length; i++) {
    if (arr[i] === x) {
      return i
    }
  }
  return -1
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Insertion sort example&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// quadratic time
function insertionSort(arr: number[]) {
  for (let i = 1; i &amp;lt; arr.length; i++) {
    let currentVal = arr[i]
    for (var j = i - 1; j &amp;gt;= 0 &amp;amp;&amp;amp; arr[j] &amp;gt; currentVal; j--) {
      arr[j + 1] = arr[j]
    }
    arr[j + 1] = currentVal

    console.log(arr)
  }

  return arr
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Both codes work and solve the proposed problem, but one is more performative than the other. The first code is linear, which means that the for loop will be executed a number of times directly proportional to the size of the array. This means that if the array has n elements, the loop will be executed n times, which can be represented by Θ(n).&lt;/p&gt;
&lt;p&gt;The advantage of this approach is that, in case of larger arrays, the code will run faster, as the number of iterations is proportional to the size of the array. This makes the time complexity of the code limited by the size of the array, resulting in a less steep growth chart compared to a code with quadratic complexity. In other words, the first code is more performative and efficient in situations where the array can be very large.&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;/introbigo/bigo.jpg&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;The second code is an example of quadratic complexity Θ(n²). This means that the for loop inside the for loop will be executed a number of times proportional to the square of the size of the array. In other words, if the array has n elements, the inner loop will be executed n * n times, which can be represented by Θ(n²).&lt;/p&gt;
&lt;p&gt;The implications of this complexity are that, in larger arrays, the growth chart will be steeper, resulting in slower and more complex code as the input gets larger.&lt;/p&gt;
&lt;p&gt;Now, let&apos;s see some examples of code with logarithmic complexities O(log(n)) and O(n log(n)).&lt;/p&gt;
&lt;p&gt;Suppose you receive a list of numbers and need to find a certain number x in the list. To do this, you can use the binary search algorithm, which has complexity O(log(n)).&lt;/p&gt;
&lt;p&gt;Another example is when you need to sort a list of numbers in a logarithmic way. To do this, you can use the merge sort algorithm, which has complexity O(n log(n)).&lt;/p&gt;
&lt;p&gt;These algorithms are more performative and efficient than linear or quadratic approaches in situations where the size of the input can be very large.&lt;/p&gt;
&lt;p&gt;Binary search example&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// O(log(n))
function binarySearch(arr: number[], x: number) {
  let left = 0
  let right = arr.length - 1

  while (left &amp;lt;= right) {
    let mid = Math.floor((left + right) / 2)
    if (arr[mid] === x) {
      return mid
    }
    if (arr[mid] &amp;lt; x) {
      left = mid + 1
    } else {
      right = mid - 1
    }
  }
  return -1
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Merge sort example&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// O(n log(n))
function mergeSort(arr: number[]) {
  if (arr.length === 1) {
    return arr
  }

  let mid = Math.floor(arr.length / 2)
  let left = arr.slice(0, mid)
  let right = arr.slice(mid)

  return merge(mergeSort(left), mergeSort(right))
}

function merge(left: number[], right: number[]) {
  let result = []
  let i = 0
  let j = 0

  while (i &amp;lt; left.length &amp;amp;&amp;amp; j &amp;lt; right.length) {
    if (left[i] &amp;lt; right[j]) {
      result.push(left[i])
      i++
    } else {
      result.push(right[j])
      j++
    }
  }

  return result.concat(left.slice(i)).concat(right.slice(j))
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Both examples are valid, but they have different time complexities. The first one is O(log(n)), which means that the running time increases logarithmically with respect to the size of the input. In other words, in the worst case, if the array has 8 elements, the algorithm will be executed 3 times. For example: log2(8) = 3.&lt;/p&gt;
&lt;p&gt;The second example, O(n log(n)), is a notation that indicates that the running time of an algorithm increases proportionally to the product of the size of the input data and the logarithm of that size. This means that, in the worst case, if the array has 8 elements, the algorithm will be executed 24 times. For example: 8 * log2(8) = 24.&lt;/p&gt;
&lt;p&gt;However, it is important to remember that the temporal complexity of an algorithm does not necessarily imply higher or lower speed. It is possible that an algorithm with worse complexity is faster than an algorithm with better complexity, depending on the specific input. However, in general, it is safe to say that the lower the complexity, the faster the algorithm will be, as the input increases.&lt;/p&gt;
&lt;p&gt;For those who want to delve deeper into the subject, I recommend reading the book “Introduction to Algorithms” by Thomas H. Cormen.&lt;/p&gt;
</content:encoded></item><item><title>Proving natural numbers are infinity in Coq</title><link>https://vitorsalmeida.com/proving-naturals-infinity/</link><guid isPermaLink="true">https://vitorsalmeida.com/proving-naturals-infinity/</guid><pubDate>Sun, 13 Aug 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;An interesting fact about math is that you can demonstrate things. A lot of people thinks that demonstrate is exemplify something, but it is not. It&apos;s common to see people saying that $1 + 1 = 2$, just like one apple plus one apple is two apples. But, this is not a demonstration. A demonstration is a logical proof that something is true, by a formal way. You cannot say that every 1 plus itself is equal to two. Boolean algebra is a good example of this. In Boolean algebra, $1 + 1 = 1$. So, you can&apos;t say that aways $1 + 1 = 2$.&lt;/p&gt;
&lt;h2&gt;What is Coq?&lt;/h2&gt;
&lt;p&gt;Coq is a software that allows you to write proofs. It is a proof assistant based on the calculus of inductive constructions. It is a functional programming language based on lambda calculus. I won&apos;t talk about lambda calculus and the calculus of inductive constructions, but you can find more information about them on Wikipedia.&lt;/p&gt;
&lt;h2&gt;What is a formal proof?&lt;/h2&gt;
&lt;p&gt;A formal proof is a process based on logical rules and axioms used to demonstrate some theorem. The goal is to show an absolute truth that some affirmation is faithful by following strict rules.&lt;/p&gt;
&lt;h2&gt;The proof&lt;/h2&gt;
&lt;p&gt;A simple way to prove that natural numbers are infinite is by defining that given a natural number ($n: nat$), $n + 1$ will return natural numbers greater than $n$.&lt;/p&gt;
&lt;p&gt;You need to make it because this theorem proves that natural numbers are infinite by showing that given &lt;strong&gt;any&lt;/strong&gt; natural numbers, you can &lt;strong&gt;always&lt;/strong&gt; find a natural number greater than it, by summing $1$ to it.&lt;/p&gt;
&lt;p&gt;So, lets to define our theorem in Coq:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Theorem plus_1_natural : forall n : nat, 1 + n = S n /\ S n &amp;gt; n.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;forall&lt;/code&gt; keyword means the theorem is valid for all natural numbers. The $\land$ means the theorem is a conjunction, and the $S$ is the successor function. So, the theorem says that given a natural number $n$, $1 + n$ is equal to $S n$, and $S n$ is greater than $n$.&lt;/p&gt;
&lt;p&gt;Lets to prove the theorem:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Proof.
  intros n.
  split.
  - reflexivity.
  - apply le_n_S. apply le_n.
Qed.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;intros n&lt;/code&gt; introduces the universal quantifier &lt;code&gt;forall&lt;/code&gt; and the arbitrary natural number $n$ as a variable. The &lt;code&gt;split.&lt;/code&gt; splits the objective into two subgoals. One for each conjunct $\land$. The hyphen is to refer to the subgoal. The &lt;code&gt;reflexivity.&lt;/code&gt; is to prove that something equals to itself. Like $1 + n = S n$, because $1 + n$ is equal to $S n$. You can reduce it, like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;$1 + 1 = 2$.&lt;/li&gt;
&lt;li&gt;$2 = 2$.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Coq have some macros that abstracts some proofs. We can see this reducing using &lt;code&gt;simpl.&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;/natinfinity/simpl.gif&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;Coq gives us some theorems related to natural numbers ordering, like &lt;code&gt;le_n_S&lt;/code&gt; and &lt;code&gt;le_n&lt;/code&gt;. These theorems can be used to reduce steps of our proof, replacing them with the applied theorem. In math, the &lt;code&gt;le&lt;/code&gt; it is $\leq$, less than or equal.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;le_n&lt;/code&gt; any natural number is less than or equal to itself. $n \leq n$ is a true statement for any natural number $n$.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;le_n_S&lt;/code&gt; says that if $n \leq m$, then $S n \leq S m$. So, if $n \leq n$, then $S n \leq S n$.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;code&gt;apply le_n_S.&lt;/code&gt; definition is &lt;code&gt;le_n_S : forall n m : nat, n &amp;lt;= m -&amp;gt; S n &amp;lt;= S m&lt;/code&gt;, and &lt;code&gt;le_n&lt;/code&gt; that &lt;code&gt;le_n : forall n : nat, n &amp;lt;= n&lt;/code&gt;. So, to read the &lt;code&gt;apply le_n_S. apply le_n.&lt;/code&gt; is: &quot;Given a natural number &lt;code&gt;n&lt;/code&gt;, if &lt;code&gt;n &amp;lt;= n&lt;/code&gt;, then &lt;code&gt;S n &amp;lt;= S n&lt;/code&gt;&quot;. And this is true, because &lt;code&gt;n &amp;lt;= n&lt;/code&gt; is true, and &lt;code&gt;S n &amp;lt;= S n&lt;/code&gt; is true too. So, the theorem is proved. &lt;code&gt;Qed.&lt;/code&gt; means that the proof is finished.&lt;/p&gt;
&lt;p&gt;You can also see it on CoqIDE:&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;/natinfinity/le.gif&quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;You can also show the output of the proof and run the proof. So, lets do it.&lt;/p&gt;
&lt;p&gt;You can use &lt;code&gt;Print&lt;/code&gt; to show the definition of some theorem, and &lt;code&gt;Eval compute in&lt;/code&gt; to run the proof. In the end, our proof will be:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Theorem plus_1_natural : forall n : nat, 1 + n = S n /\ S n &amp;gt; n.
Proof.
  intros n.
  split.
  - reflexivity.
  - apply le_n_S. apply le_n.
Qed.
Print plus_1_natural.
Eval compute in (plus_1_natural 1).
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And the output will be:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;plus_1_natural = fun n : nat =&amp;gt; conj eq_refl (le_n_S n n (le_n n))
: forall n : nat, 1 + n = S n /\ S n &amp;gt; n
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;= plus_1_natural 1
: 1 + 1 = 2 /\ 2 &amp;gt; 1
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Building a JSON Parser from scratch with JS</title><link>https://vitorsalmeida.com/building-json-parser-from-scratch/</link><guid isPermaLink="true">https://vitorsalmeida.com/building-json-parser-from-scratch/</guid><pubDate>Sat, 12 Aug 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;A parser can have various applications in everyday life, and you probably use some parser daily. &lt;a href=&quot;https://babeljs.io/&quot;&gt;Babel&lt;/a&gt;, &lt;a href=&quot;https://webpack.js.org/&quot;&gt;webpack&lt;/a&gt;, &lt;a href=&quot;https://eslint.org/&quot;&gt;eslint&lt;/a&gt;, &lt;a href=&quot;https://prettier.io/&quot;&gt;prettier&lt;/a&gt;, and &lt;a href=&quot;https://github.com/facebook/jscodeshift&quot;&gt;jscodeshift&lt;/a&gt;. All of them, behind the scenes, run a parser that manipulates an Abstract Syntax Tree (AST) to do what you need - we&apos;ll talk about that later, don&apos;t worry.&lt;/p&gt;
&lt;p&gt;The idea of this text is to introduce the concept of lexing and parsing, implementing them using JavaScript to analyze expressions in JSON. The goal will be to separate this process into functions, explain these functions, and, in the end, have you implement a JSON parser generating an AST.&lt;/p&gt;
&lt;p&gt;It&apos;s worth noting that my repository is open, and you can access it &lt;a href=&quot;https://github.com/vit0rr/json-parser&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Lexing&lt;/h2&gt;
&lt;p&gt;A &lt;code&gt;lexer&lt;/code&gt; will be responsible for converting an expression, whatever it may be, into tokens. These tokens are identifiable elements that have an assigned meaning.&lt;/p&gt;
&lt;p&gt;You can divide these tokens in several ways, such as &lt;a href=&quot;https://en.wikipedia.org/wiki/Identifier_(computer_languages)&quot;&gt;identifier&lt;/a&gt;, &lt;a href=&quot;https://en.wikipedia.org/wiki/Reserved_word&quot;&gt;keyword&lt;/a&gt;, &lt;a href=&quot;https://en.wikipedia.org/wiki/Delimiter&quot;&gt;delimiter&lt;/a&gt;, &lt;a href=&quot;https://en.wikipedia.org/wiki/Operator_(computer_programming)&quot;&gt;operator&lt;/a&gt;, &lt;a href=&quot;https://en.wikipedia.org/wiki/Literal_(computer_programming)&quot;&gt;literal&lt;/a&gt;, and &lt;a href=&quot;https://en.wikipedia.org/wiki/Comment_(computer_programming)&quot;&gt;comment&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;{ &quot;type&quot;: &quot;LEFT_BRACE&quot;, &quot;value&quot;: undefined }&lt;/code&gt; is an example of a delimiter. &lt;code&gt;{ &quot;type&quot;: &quot;STRING&quot;, &quot;value&quot;: &quot;name&quot; }&lt;/code&gt; is an example of a literal.&lt;/p&gt;
&lt;p&gt;Example: &lt;code&gt;{&quot;name&quot;:&quot;Vitor&quot;,&quot;age&quot;:18}&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[
  { &quot;type&quot;: &quot;LEFT_BRACE&quot;, &quot;value&quot;: undefined },
  { &quot;type&quot;: &quot;STRING&quot;, &quot;value&quot;: &quot;name&quot; },
  { &quot;type&quot;: &quot;COLON&quot;, &quot;value&quot;: undefined },
  { &quot;type&quot;: &quot;STRING&quot;, &quot;value&quot;: &quot;Vitor&quot; },
  { &quot;type&quot;: &quot;COMMA&quot;, &quot;value&quot;: undefined },
  { &quot;type&quot;: &quot;STRING&quot;, &quot;value&quot;: &quot;age&quot; },
  { &quot;type&quot;: &quot;COLON&quot;, &quot;value&quot;: undefined },
  { &quot;type&quot;: &quot;NUMBER&quot;, &quot;value&quot;: &quot;18&quot; },
  { &quot;type&quot;: &quot;RIGHT_BRACE&quot;, &quot;value&quot;: undefined }
]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that in lexical analysis, you separate your expression into tokens, and each token has its identification.&lt;/p&gt;
&lt;p&gt;To code this, let&apos;s first understand what we will be doing exactly. The idea of the &lt;code&gt;lexer&lt;/code&gt; function is to receive an argument of type String and return an Array of tokens, which will be our JSON divided into specific types of information, as we have already seen and discussed.&lt;/p&gt;
&lt;p&gt;To achieve this, we will create a variable called &lt;code&gt;current&lt;/code&gt;, which will store the current position of the character in the &lt;code&gt;input&lt;/code&gt; being analyzed by the &lt;code&gt;lexer&lt;/code&gt;. In other words, it represents the position it is currently at in our JSON. Additionally, we will have a constant called &lt;code&gt;tokens&lt;/code&gt;, which will be an array that will hold all of our tokens in the end.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export const lexer = (input: string): Token[] =&amp;gt; {
let current = 0;
const tokens: Token:[] = [];


}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, we need to run a loop that will iterate until all the characters of the input have been processed.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note that it&apos;s possible to refactor all these &lt;code&gt;if&lt;/code&gt; blocks into a &lt;code&gt;switch&lt;/code&gt; statement. However, I followed an imperative style.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;export const lexer = (input: string): Token[] =&amp;gt; {
  let current = 0
  const tokens: Token[] = []


  while (current &amp;lt; input.length) {
    let char = input[current]


    if (char === &apos;{&apos;) {
      tokens.push(createToken(TOKEN_TYPES.LEFT_BRACE))
      current++
      continue
    }


    if (char === &apos;}&apos;) {
      tokens.push(createToken(TOKEN_TYPES.RIGHT_BRACE))
      current++
      continue
    }


    if (char === &apos;[&apos;) {
      tokens.push(createToken(TOKEN_TYPES.LEFT_BRACKET))
      current++
      continue
    }


    if (char === &apos;]&apos;) {
      tokens.push(createToken(TOKEN_TYPES.RIGHT_BRACKET))
      current++
      continue
    }


    if (char === &apos;:&apos;) {
      tokens.push(createToken(TOKEN_TYPES.COLON))
      current++
      continue
    }


    if (char === &apos;,&apos;) {
      tokens.push(createToken(TOKEN_TYPES.COMMA))
      current++
      continue
    }


    const WHITESPACE = /\s/


    if (WHITESPACE.test(char)) {
      current++
      continue
    }


    const NUMBERS = /[0-9]/
    if (NUMBERS.test(char)) {
      let value = &apos;&apos;
      while (NUMBERS.test(char)) {
        value += char


        char = input[++current]
      }
      tokens.push(createToken(TOKEN_TYPES.NUMBER, value))
      continue
    }


    if (char === &apos;&quot;&apos;) {
      let value = &apos;&apos;
      char = input[++current]
      while (char !== &apos;&quot;&apos;) {
        value += char
        char = input[++current]
      }
      char = input[++current]
      tokens.push(createToken(TOKEN_TYPES.STRING, value))
      continue
    }


    if (
      char === &apos;t&apos; &amp;amp;&amp;amp;
      input[current + 1] === &apos;r&apos; &amp;amp;&amp;amp;
      input[current + 2] === &apos;u&apos; &amp;amp;&amp;amp;
      input[current + 3] === &apos;e&apos;
    ) {
      tokens.push(createToken(TOKEN_TYPES.TRUE))
      current += 4
      continue
    }


    if (
      char === &apos;f&apos; &amp;amp;&amp;amp;
      input[current + 1] === &apos;a&apos; &amp;amp;&amp;amp;
      input[current + 2] === &apos;l&apos; &amp;amp;&amp;amp;
      input[current + 3] === &apos;s&apos; &amp;amp;&amp;amp;
      input[current + 4] === &apos;e&apos;
    ) {
      tokens.push(createToken(TOKEN_TYPES.FALSE))
      current += 5
      continue
    }


    if (
      char === &apos;n&apos; &amp;amp;&amp;amp;
      input[current + 1] === &apos;u&apos; &amp;amp;&amp;amp;
      input[current + 2] === &apos;l&apos; &amp;amp;&amp;amp;
      input[current + 3] === &apos;l&apos;
    ) {
      tokens.push(createToken(TOKEN_TYPES.NULL))
      current += 4
      continue
    }


    throw new TypeError(&apos;I dont know what this character is: &apos; + char)
  }


  return tokens
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This code may seem complicated, but it&apos;s actually quite simple. Inside my loop, I start by defining the variable &lt;code&gt;char&lt;/code&gt;, which will store the character currently being analyzed in that iteration of the loop. Then, for each type of character, we want a specific action.&lt;/p&gt;
&lt;p&gt;If the &lt;code&gt;char&lt;/code&gt; is equal to &lt;code&gt;{&lt;/code&gt;, we push into the tokens array, passing to it the ENUM of Tokens, which will be the name of each token.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export enum TOKEN_TYPES {
  LEFT_BRACE = &apos;LEFT_BRACE&apos;,
  RIGHT_BRACE = &apos;RIGHT_BRACE&apos;,
  LEFT_BRACKET = &apos;LEFT_BRACKET&apos;,
  RIGHT_BRACKET = &apos;RIGHT_BRACKET&apos;,
  COLON = &apos;COLON&apos;,
  COMMA = &apos;COMMA&apos;,
  STRING = &apos;STRING&apos;,
  NUMBER = &apos;NUMBER&apos;,
  TRUE = &apos;TRUE&apos;,
  FALSE = &apos;FALSE&apos;,
  NULL = &apos;NULL&apos;,
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the function &lt;code&gt;createToken&lt;/code&gt;, it only returns an object with the &lt;code&gt;type&lt;/code&gt; or &lt;code&gt;value&lt;/code&gt; if it exists.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export const createToken = (type: TOKEN_TYPES, value?: string): Token =&amp;gt; {
  return {
    type,
    value,
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After that, it increments +1 to the &lt;code&gt;current&lt;/code&gt; variable to move to the next &lt;code&gt;char&lt;/code&gt; in our string. This process is quite repetitive and straightforward. I won&apos;t explain each one of them, but it&apos;s worth paying attention to those that deviate a bit from the pattern.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if (char === &apos;&quot;&apos;) {
  let value = &apos;&apos;
  char = input[++current]
  while (char !== &apos;&quot;&apos;) {
    value += char
    char = input[++current]
  }


  char = input[++current]
  tokens.push(createToken(TOKEN_TYPES.STRING, value))
  continue
}


if (
  char === &apos;t&apos; &amp;amp;&amp;amp;
  input[current + 1] === &apos;r&apos; &amp;amp;&amp;amp;
  input[current + 2] === &apos;u&apos; &amp;amp;&amp;amp;
  input[current + 3] === &apos;e&apos;
) {
  tokens.push(createToken(TOKEN_TYPES.TRUE))
  current += 4
  continue
}


if (
  char === &apos;f&apos; &amp;amp;&amp;amp;
  input[current + 1] === &apos;a&apos; &amp;amp;&amp;amp;
  input[current + 2] === &apos;l&apos; &amp;amp;&amp;amp;
  input[current + 3] === &apos;s&apos; &amp;amp;&amp;amp;
  input[current + 4] === &apos;e&apos;
) {
  tokens.push(createToken(TOKEN_TYPES.FALSE))
  current += 5
  continue
}


if (
  char === &apos;n&apos; &amp;amp;&amp;amp;
  input[current + 1] === &apos;u&apos; &amp;amp;&amp;amp;
  input[current + 2] === &apos;l&apos; &amp;amp;&amp;amp;
  input[current + 3] === &apos;l&apos;
) {
  tokens.push(createToken(TOKEN_TYPES.NULL))
  current += 4
  continue
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the &lt;code&gt;char&lt;/code&gt; is equal to &lt;code&gt;&quot;&lt;/code&gt;, we enter a loop to continue reading the subsequent characters until we find another quotation mark, as this indicates the end of the string. All the characters read during this loop are concatenated into the &quot;value&quot; variable. Then, a new token is added to the &quot;tokens&quot; array.&lt;/p&gt;
&lt;p&gt;The other lines are used to set boolean values. If the current character is &quot;f&quot; and the subsequent characters form the word &lt;code&gt;false&lt;/code&gt;, we add false to the &lt;code&gt;tokens&lt;/code&gt; array. The same process is repeated for &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In all cases, the &lt;code&gt;current&lt;/code&gt; variable is incremented to point to the next character to be processed, just like we did throughout the rest of our code.&lt;/p&gt;
&lt;h2&gt;Parsing&lt;/h2&gt;
&lt;p&gt;A parser is responsible for transforming a sequence of tokens into a data structure, in this case, an &lt;a href=&quot;https://en.wikipedia.org/wiki/Abstract_syntax_tree&quot;&gt;AST&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Illustration of an AST taken from the book &quot;Modern Compiler Implementation in ML.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yafyeyuq1phdzbjhhnud.png&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;An Abstract Syntax Tree (AST) is a data structure that represents the syntactic structure of a program. Within the AST, there are several nodes, and each node represents a valid syntactic construct of the program. For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;parser: {
  &quot;type&quot;: &quot;Program&quot;,
  &quot;body&quot;: [
    {
      &quot;type&quot;: &quot;ObjectExpression&quot;,
      &quot;properties&quot;: [
        {
          &quot;type&quot;: &quot;Property&quot;,
          &quot;key&quot;: {
            &quot;type&quot;: &quot;STRING&quot;,
            &quot;value&quot;: &quot;name&quot;
          },
          &quot;value&quot;: {
            &quot;type&quot;: &quot;StringLiteral&quot;,
            &quot;value&quot;: &quot;Vitor&quot;
          }
        },
        {
          &quot;type&quot;: &quot;Property&quot;,
          &quot;key&quot;: {
            &quot;type&quot;: &quot;STRING&quot;,
            &quot;value&quot;: &quot;age&quot;
          },
          &quot;value&quot;: {
            &quot;type&quot;: &quot;NumberLiteral&quot;,
            &quot;value&quot;: &quot;18&quot;
          }
        }
      ]
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is the AST of the JSON I exemplified at the beginning. In this example, we have a total of 8 nodes. The &lt;code&gt;Program&lt;/code&gt; node represents the main program. &lt;code&gt;ObjectExpression&lt;/code&gt; represents an object, &lt;code&gt;Property&lt;/code&gt; represents a property within an object, consisting of a key and a value. &lt;code&gt;STRING&lt;/code&gt; represents a string used as a key, StringLiteral represents a string within a property, and &lt;code&gt;NumberLiteral&lt;/code&gt; represents a numeric value within a property.&lt;/p&gt;
&lt;p&gt;Through an AST, it&apos;s possible to optimize code, transform one code into another, perform static analysis, generate code, and more. For example, you could implement a new syntax, create a parser, and generate JavaScript code that would execute normally.&lt;/p&gt;
&lt;p&gt;To generate our AST, we will need a function that receives our array of tokens, iterates through it, and generates the AST according to the tokens it encounters. For this purpose, we will create a function called &lt;code&gt;walk&lt;/code&gt;, which will traverse the tokens and return the nodes of the AST.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export const parser = (tokens: Array&amp;lt;{ type: string; value?: any }&amp;gt;) =&amp;gt; {
    let current = 0;


    const walk = () =&amp;gt; {
        let token = tokens[current];


        if (token.type === TOKEN_TYPES.LEFT_BRACE) {
            token = tokens[++current];


            const node: {
                type: string;
                properties?: Array&amp;lt;{ type: string; key: any; value: any }&amp;gt;;
            } = {
                type: &apos;ObjectExpression&apos;,
                properties: [],
            };


            while (token.type !== TOKEN_TYPES.RIGHT_BRACE) {
                const property: { type: string; key: any; value: any } = {
                    type: &apos;Property&apos;,
                    key: token,
                    value: null,
                };


                token = tokens[++current];


                token = tokens[++current];
                property.value = walk();
                node.properties.push(property);


                token = tokens[current];
                if (token.type === TOKEN_TYPES.COMMA) {
                    token = tokens[++current];
                }
            }


            current++;
            return node;
        }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first check we perform is whether the current token is &lt;code&gt;{&lt;/code&gt;. If it is, we create a new node of type &lt;code&gt;ObjectExpression&lt;/code&gt; and iterate through the following tokens, adding them as properties of the object until we find the end of the key, which is &lt;code&gt;}&lt;/code&gt;. Each property is represented by a node of type &lt;code&gt;Property&lt;/code&gt;. This &lt;code&gt;Property&lt;/code&gt; type has a value that is generated by the &lt;code&gt;walk()&lt;/code&gt; function, which is called recursively.&lt;/p&gt;
&lt;p&gt;Note that I use &lt;code&gt;tokens[++current]&lt;/code&gt;. This is to advance the cursor to the next token. And if a token of type , (comma) is found, we advance the cursor again to skip the comma.&lt;/p&gt;
&lt;p&gt;The rest of the code is quite similar to what I&apos;ve just explained, so it&apos;s worth the effort to look and try to understand or implement the rest on your own. It&apos;s not very complex.&lt;/p&gt;
&lt;p&gt;Finally, I create the constant &lt;code&gt;ast&lt;/code&gt;, which will contain the type &lt;code&gt;Program&lt;/code&gt; and the body of the AST, generated by the &lt;code&gt;walk()&lt;/code&gt; function. The while loop ensures that &lt;code&gt;current&lt;/code&gt; does not exceed the size of the tokens array.&lt;/p&gt;
&lt;p&gt;After that, we simply return the AST.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export const parser = (tokens: Array&amp;lt;{ type: string; value?: any }&amp;gt;) =&amp;gt; {
  let current = 0


  const walk = () =&amp;gt; {
    let token = tokens[current]


    if (token.type === TOKEN_TYPES.LEFT_BRACE) {
      token = tokens[++current]


      const node: {
        type: string
        properties?: Array&amp;lt;{ type: string; key: any; value: any }&amp;gt;
      } = {
        type: &apos;ObjectExpression&apos;,
        properties: [],
      }


      while (token.type !== TOKEN_TYPES.RIGHT_BRACE) {
        const property: { type: string; key: any; value: any } = {
          type: &apos;Property&apos;,
          key: token,
          value: null,
        }


        token = tokens[++current]


        token = tokens[++current]
        property.value = walk()
        node.properties.push(property)


        token = tokens[current]
        if (token.type === TOKEN_TYPES.COMMA) {
          token = tokens[++current]
        }
      }


      current++
      return node
    }


    if (token.type === TOKEN_TYPES.RIGHT_BRACE) {
      current++
      return {
        type: &apos;ObjectExpression&apos;,
        properties: [],
      }
    }


    if (token.type === TOKEN_TYPES.LEFT_BRACKET) {
      token = tokens[++current]


      const node: {
        type: string
        elements?: Array&amp;lt;{ type?: string; value?: any }&amp;gt;
      } = {
        type: &apos;ArrayExpression&apos;,
        elements: [],
      }


      while (token.type !== TOKEN_TYPES.RIGHT_BRACKET) {
        node.elements.push(walk())
        token = tokens[current]


        if (token.type === TOKEN_TYPES.COMMA) {
          token = tokens[++current]
        }
      }


      current++
      return node
    }


    if (token.type === TOKEN_TYPES.STRING) {
      current++
      return {
        type: &apos;StringLiteral&apos;,
        value: token.value,
      }
    }


    if (token.type === TOKEN_TYPES.NUMBER) {
      current++
      return {
        type: &apos;NumberLiteral&apos;,
        value: token.value,
      }
    }


    if (token.type === TOKEN_TYPES.TRUE) {
      current++
      return {
        type: &apos;BooleanLiteral&apos;,
        value: true,
      }
    }


    if (token.type === TOKEN_TYPES.FALSE) {
      current++
      return {
        type: &apos;BooleanLiteral&apos;,
        value: false,
      }
    }


    if (token.type === TOKEN_TYPES.NULL) {
      current++
      return {
        type: &apos;NullLiteral&apos;,
        value: null,
      }
    }


    throw new TypeError(token.type)
  }


  const ast = {
    type: &apos;Program&apos;,
    body: [],
  }


  while (current &amp;lt; tokens.length) {
    ast.body.push(walk())
  }


  return ast
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;const tokens = lexer(&apos;{&quot;name&quot;:&quot;Vitor&quot;,&quot;age&quot;:18}&apos;)
console.log(&apos;tokens&apos;, tokens)
const json = parser(tokens)


console.log(&apos;parser:&apos;, JSON.stringify(json, null, 2))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Parsing can be fun, but in reality, it&apos;s not often written, and you probably don&apos;t need to write your own parser. If you are implementing a programming language, for example, there are already various tools that can do this job for you, such as OCamllex, Menhir, or Nearley.&lt;/p&gt;
&lt;p&gt;It&apos;s also essential to note that, as this is an introductory article, I didn&apos;t cover the different tokenization and parsing techniques, such as &lt;a href=&quot;https://en.wikipedia.org/wiki/LR_parser&quot;&gt;LR(0)&lt;/a&gt;, &lt;a href=&quot;https://en.wikipedia.org/wiki/Canonical_LR_parser&quot;&gt;LR(1)&lt;/a&gt;, &lt;a href=&quot;https://en.wikipedia.org/wiki/SLR_grammar&quot;&gt;SLR(1)&lt;/a&gt;, etc. However, be aware that these techniques exist, and you can research more about them. There are also many books that cover these topics.&lt;/p&gt;
&lt;p&gt;If you want to see how the AST of popular languages looks, I recommend the &lt;a href=&quot;https://astexplorer.net/&quot;&gt;AST Explorer&lt;/a&gt;. It supports various languages, and you can view the complete AST and navigate through the nodes. If you want to go further, you can try to copy some logic from an existing parser and implement it in your own, such as calculating an expression according to precedence order, for example: &lt;code&gt;1 + 2 * 3&lt;/code&gt; (which is 7, not 9).&lt;/p&gt;
&lt;p&gt;If you&apos;re interested in learning more, I recommend the book &quot;Modern Compiler Implementation in ML.&quot; Despite the title being in ML, you can study from it without necessarily writing ML code, as there are other versions written in C, C++, and Java.&lt;/p&gt;
</content:encoded></item><item><title>Introduction to Machine Code</title><link>https://vitorsalmeida.com/introduction-to-machine-code/</link><guid isPermaLink="true">https://vitorsalmeida.com/introduction-to-machine-code/</guid><pubDate>Sun, 03 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h3&gt;What is machine code?&lt;/h3&gt;
&lt;p&gt;Machine code is computer code.&lt;/p&gt;
&lt;p&gt;It consisting of machine language instructions that will be used to control a CPU. And about this, the reason that some programming languages have virtual machines, is to simulate a unique CPU. It helps to run the same code on different platforms without needing to care about specific architectures.&lt;/p&gt;
&lt;p&gt;An opcode (Operation Code) is one byte wide, has an arbitrary but unique value, and is the first byte on the instruction.&lt;/p&gt;
&lt;p&gt;Opcodes are just a set that specifies the operation (op) to be performed. In other words, an opcode is just an instruction machine code.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;MOV AL, 34h
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The opcode here is the MOV instruction. The other parts are the operands. In this case, the operands are the register AL and the value 34 hex. However, the operands are the parameters for the instruction. If it needs clarification, it could be more helpful to think about math. Think about &lt;strong&gt;3 + 6 = 9&lt;/strong&gt;.
The &quot;opcode&quot; here, the operation, is the &lt;strong&gt;+&lt;/strong&gt; symbol, which means addition. The operands are &lt;strong&gt;3&lt;/strong&gt; and &lt;strong&gt;6&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;Assembler, disassembler&lt;/h3&gt;
&lt;p&gt;Of course, no one writes code in machine code — at least not in modern times. However, some tools translate code from Assembly Language to Machine Code.&lt;/p&gt;
&lt;p&gt;In assembly language, each instruction on the computer is represented by a mnemonic:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;section .data
	    msg db &quot;Result: &quot;         ; message to display
    number db &apos;0&apos;                 ; single character to store the result

section .text
    global _start

_start:
    mov rax, 1                    ; Put number 1 in rax
    add rax, 2                    ; Add 2 to it

    ; Step 2: Convert result to character
    add rax, &apos;0&apos;                  ; Convert number to ASCII character
    mov [number], al              ; Store the character

    ; Step 3: Print &quot;Result: &quot;
    mov rax, 1                    ; System call for write
    mov rdi, 1                    ; File descriptor 1 is stdout
    mov rsi, msg                  ; Address of our message
    mov rdx, 8                    ; Message length
    syscall

    ; Step 4: Print the number
    mov rax, 1                    ; System call for write
    mov rdi, 1                    ; File descriptor 1 is stdout
    mov rsi, number               ; Address of our number
    mov rdx, 1                    ; Length is 1 character
    syscall

    ; Step 5: Exit program
    mov rax, 60                   ; System call for exit
    xor rdi, rdi                  ; Return code 0
    syscall
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This code was written using an assembler/disassembler called NASM and sums &lt;strong&gt;1&lt;/strong&gt; to &lt;strong&gt;2&lt;/strong&gt;. This program (NASM) will translate the mnemonics into machine code. Historically, this part is exciting. If you think about how things work nowadays, we already have compilers and all the tools needed to write code that humans can easily understand. But think a second about the process of creating a new programming language.&lt;/p&gt;
&lt;p&gt;Of course, you will need some language to code your new language. For example, I&apos;m writing a toy language called Monkey using GoLang. If you&apos;re creating a language for production use, eventually, you&apos;ll do the bootstrapping — basically, implement your language using the language itself. Following my example of Monkey, it is like refactoring Monkey using Monkey instead of GoLang.&lt;/p&gt;
&lt;p&gt;Now, moving some years back, someone needed to write the first assembler/disassembler directly in a binary system. The first functional computer will depend on what you understand by computer and functional and other things. But I&apos;ll follow the idea that it was the &lt;a href=&quot;https://en.wikipedia.org/wiki/EDSAC&quot;&gt;EDSAC&lt;/a&gt;. Initially, from binary input, programs were entered using a set of 18 switches on EDSAC&apos;s control panel. Each instruction was typically 18 bits long - that&apos;s not 18 because the topmost was always unavailable due to timing programs, so only 17 bits were used. Operators would physically flip these switches to represent 1s and 0s. And, of course, they used paper tape for program storage, but the initial loader had to be entered manually.
Then, the first simple assembler. This was a tiny 31-instruction program (31 words). It was the world&apos;s first assembler. It could read paper tape and convert simple symbolic notations into machine code. The basic format was like a Letter + Number, like A 32, which means &quot;Add the content of memory location 32&quot;.&lt;/p&gt;
&lt;p&gt;The first bootstrap comes from this basic assembler. The new version could handle more symbols, basic arithmetic in the address, and some macros. This process was repeated. If you want to learn more about this, I recommend reading &lt;a href=&quot;https://pt.wikipedia.org/wiki/EDSAC&quot;&gt;EDSAC&lt;/a&gt; from Wikipedia and watching &lt;a href=&quot;https://youtu.be/nc2q4OOK6K8?si=W7rzWRtFQDXfqA2z&quot;&gt;Bootstrapping EDSAC: Initial Orders—Computerphile&lt;/a&gt; from Computerphile.&lt;/p&gt;
&lt;h3&gt;Starting with bytes&lt;/h3&gt;
&lt;p&gt;Excellent. I hope I have cleared your mind about machine code at this point. We&apos;ll write some code in GoLang to write our machine code instructions.&lt;/p&gt;
&lt;p&gt;Let&apos;s start by defining the bytecode format:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;package code

type Instructions []byte // byte is an alias for uint8

type Opcode byte
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;Instructions&lt;/code&gt; are a slice of bytes, and an &lt;code&gt;Opcode&lt;/code&gt; is a byte. Note how it describes our past descriptions. Let&apos;s define the first opcode, which would tells the VM (or the processor, if you&apos;re compiling directly to machine code and to some especific processor architecture) to push something on the stack - we&apos;ll not build a VM at this article.&lt;/p&gt;
&lt;p&gt;Backing to the opcode, it wouldn&apos;t be called &quot;push&quot;, because it won&apos;t be solely about pushing things. Let&apos;s think about the expression &lt;code&gt;1 + 2&lt;/code&gt;. There are three instructions, two of which tell the VM/processor to push &lt;code&gt;1&lt;/code&gt; and &lt;code&gt;2&lt;/code&gt; to the stack. A first instinct might tell us to implement these by defining a &quot;push&quot; instruction with an integer as its operand, with the idea being that the VM/processor then takes the integer operand and pushes it into the stack. For integers, it would work because I could encode them and put them directly into the bytecode, but for string literals, for example, putting those into the bytecode is also possible since it&apos;s just made of bytes. Still, it would also be a lot of bloat and would sooner or later become unwieldy.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Variable size: A string can be any length, like &quot;a&quot; or &quot;a more extensive text like this one.&quot;;&lt;/li&gt;
&lt;li&gt;You&apos;d have multiple copies of the same string if it appears several times;&lt;/li&gt;
&lt;li&gt;Bytecode loading performance would be impacted;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Notice how it is a bad design? Here, I introduce the idea of &lt;code&gt;constants&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Constants&lt;/h3&gt;
&lt;p&gt;In this context, &lt;code&gt;constants&lt;/code&gt; are short for &quot;constant expressions&quot; and refer to expressions whose value doesn&apos;t change. It is &lt;code&gt;constant&lt;/code&gt; and can be determined at &lt;code&gt;compile time&lt;/code&gt;. That means we don&apos;t need to run the program to know what these expressions evaluate. A compiler can find them in the code and store the value they evaluate. After that, it can &lt;em&gt;reference&lt;/em&gt; the constants in the instructions it generates instead of embedding the value directly in them. A plain integer does the job fine and can serve as an index into a data structure that holds all constants, often called a constant pool. For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Instead of having bytecode like:
PUSH 987654321 // Takes up a lot of space if the number is big
PUSH &quot;Hello World&quot; // Takes even more space for string

// I can have:
PUSH_CONST 0 // Where 0 is just an index into the constant pool
PUSH_CONST 1 // Much more compact

Constant Pool:
[0] -&amp;gt; 987654321
[1] -&amp;gt; &quot;Hello World&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;More compact bytecode (indices are smaller than full values)&lt;/li&gt;
&lt;li&gt;Deduplication (the same constant only needs to be stored once)&lt;/li&gt;
&lt;li&gt;Better memory usage&lt;/li&gt;
&lt;li&gt;It is easier to manage complex constants like strings&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Well, that said, let&apos;s define the &lt;code&gt;OpConstant&lt;/code&gt;. This opcode has one operand: the number I previously assigned to the constant. When the VM/processor executes &lt;code&gt;OpConstant&lt;/code&gt;, it retrieves the constant using the operand as an index and pushes it on to the stack.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// [...]

const (
	OpConstant Opcode = iota
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;Iota&lt;/code&gt; will generate increasing &lt;code&gt;byte&lt;/code&gt; values because I don&apos;t care about the actual values the opcodes represent. They only need to be distinct from each other and fit in one byte. Now, let&apos;s define the part that says &lt;code&gt;OpConstant&lt;/code&gt; has one operand.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// [...]

type Definition struct {
	Name          string
	OperandWidths []int
}

var definitions = map[Opcode]*Definition{
	OpConstant: {&quot;OpConstant&quot;, []int{2}},
}

func Lookup(op byte) (*Definition, error) {
	def, ok := definitions[Opcode(op)]
	if !ok {
		return nil, fmt.Errorf(&quot;opcode %d undefined&quot;, op)
	}
	return def, nil
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The lookup helper is not needed, but it&apos;s nice for debugging:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;debug, _ := Lookup(instructions[0])
fmt.Println(debug)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Output:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;go test ./code -v
=== RUN   TestMake
&amp;amp;{OpConstant [2]}
--- PASS: TestMake (0.00s)
PASS
ok      github.com/vit0rr/introduction-to-machine-code/code     0.179s
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;Definition&lt;/code&gt; for an &lt;code&gt;Opcode&lt;/code&gt; has two fields: &lt;code&gt;Name&lt;/code&gt; and &lt;code&gt;OperandsWidths&lt;/code&gt;. &lt;code&gt;Name&lt;/code&gt; helps to make an &lt;code&gt;Opcode&lt;/code&gt; readable and &lt;code&gt;OperandWidths&lt;/code&gt; contains the number of bytes each operand takes up.&lt;/p&gt;
&lt;p&gt;The definition for &lt;code&gt;OpConstant&lt;/code&gt; says that it&apos;s only operand two bytes wide, which makes it an uint16 and limits its maximum value to &lt;code&gt;65535&lt;/code&gt;; if it includes 0, the number of representable values is then &lt;code&gt;65536&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;With this, It&apos;s already possible to create the first bytecode instruction - finally.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// [...]

func Make(op Opcode, operands ...int) []byte {
	def, ok := definitions[op]
	if !ok {
		return []byte{}
	}

	instructionLen := 1
	for _, w := range def.OperandWidths {
		instructionLen += w
	}

	instruction := make([]byte, instructionLen)
	instruction[0] = byte(op)

	offset := 1
	for i, o := range operands {
		width := def.OperandWidths[i]
		switch width {
		case 2:
			binary.BigEndian.PutUint16(instruction[offset:], uint16(o))
		}
		offset += width
	}

	return instruction
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This function creates a bytecode instruction for the given &lt;code&gt;Opcode&lt;/code&gt; and operands. It looks up the &lt;code&gt;Definition&lt;/code&gt; for the &lt;code&gt;Opcode&lt;/code&gt; and calculates the instruction&apos;s length. Then it creates a byte slice with the correct length and sets the first byte to the &lt;code&gt;Opcode&lt;/code&gt;. The function then iterates over the operands, encoding them into the instruction according to their width.&lt;/p&gt;
&lt;p&gt;And of course we&apos;ll write tests about all this.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;package code

import &quot;testing&quot;

func TestMake(t *testing.T) {
	tests := []struct {
		op       Opcode
		operands []int
		expected []byte
	}{
		{OpConstant, []int{65534}, []byte{byte(OpConstant), 255, 254}},
	}

	for _, tt := range tests {
		instructions := Make(tt.op, tt.operands...)

		if len(instructions) != len(tt.expected) {
			t.Errorf(&quot;instructions has wrong length. want=%d, got=%d&quot;,
				len(tt.expected), len(instructions))
		}

		for i, b := range tt.expected {
			if instructions[i] != tt.expected[i] {
				t.Errorf(&quot;wrong byte at pos %d. want=%d, got=%d&quot;,
					i, b, instructions[i])
			}
		}
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I only pass &lt;code&gt;OpConstant&lt;/code&gt; and the operand &lt;code&gt;65534&lt;/code&gt; to the &lt;code&gt;Make&lt;/code&gt; function. Then expect to get back a &lt;code&gt;[]byte&lt;/code&gt; golding three bytes. The first has to be the opcode, &lt;code&gt;OpConstant&lt;/code&gt;, and the other two should be the big-endian encoding of &lt;code&gt;65534&lt;/code&gt;. And that&apos;s also why use &lt;code&gt;65534&lt;/code&gt; instead of &lt;code&gt;65535&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;65534 in decimal = 1111 1111 1111 1110 in binary
                 = 0xFF 0xFE in hexadecimal (two bytes)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Big-endian means &quot;most significant byte first&quot;. Like reading left-to-right:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In big-endian: 65534 = [0xFF, 0xFE]&lt;/li&gt;
&lt;li&gt;In little-endian: 65534 = [0xFE, 0xFF]
And &lt;code&gt;65535&lt;/code&gt;, both bytes are the same ([0xFF, 0xFF]) - can&apos;t tell the order.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The expected output is 3 bytes:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[OpConstant, 0xFF, 0xFE]
 ^           ^      ^
 |           |      Second byte of 65534
 |           First byte of 65534
 The instruction opcode
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first thing I&apos;m doing here is to determine how long the resulting instruction will take. That allows me to allocate a byte slice with the proper length. Note that I don&apos;t use the &lt;code&gt;Lookup&lt;/code&gt; function to get to the definition, which gives me a more usable function signature for Make in the tests later.&lt;/p&gt;
&lt;p&gt;As soon as we have the final value of &lt;code&gt;instructionLen&lt;/code&gt;, we allocate the instruction &lt;code&gt;[]byte&lt;/code&gt; and add the Opcode as its first byte by casting it into one. Then comes the tricky part: I iterate over the defined operand widths, taking the matching element from operands and putting it in the instructions. Depending on its width, I do that by using a switch statement with a different method for each operand.&lt;/p&gt;
&lt;p&gt;I only ensure that a two-byte operand is encoded in big-endian. After encoding the operand, I increment the offset by its width and the next iteration of the loop. Since the &lt;code&gt;OpConstant&lt;/code&gt; opcode in the test case has only one operand, the loop performs only one iteration before the &lt;code&gt;Make&lt;/code&gt; returns &lt;code&gt;instruction&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;And that&apos;s it! First bytecode instruction is done.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;go test ./code
ok      github.com/vit0rr/introduction-to-machine-code/code     0.537s
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;References:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.teach-ict.com/as_as_computing/ocr/H447/F453/3_3_8/features/miniweb/pg4.htm&quot;&gt;Opcodes and Operands&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Enumeration&quot;&gt;Enumeration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Opcode&quot;&gt;Opcode&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://archive.org/details/machine-code-for-beginners&quot;&gt;Machine Code for Beginners (Usborne Computer Books)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://compilerbook.com/&quot;&gt;Writing a Compiler in Go (by Thorsten Ball)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://minnie.tuhs.org/Tecs/book/chapter07.pdf&quot;&gt;Virtual Machine I: Stack Arithmetic (Chapter 7)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/EDSAC&quot;&gt;EDSAC&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Introduction to the Untyped Lambda Calculus (β-reduction and α-conversion)</title><link>https://vitorsalmeida.com/introduction-untyped-lc/</link><guid isPermaLink="true">https://vitorsalmeida.com/introduction-untyped-lc/</guid><pubDate>Tue, 04 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Lambda_calculus&quot;&gt;Lambda calculus&lt;/a&gt; is a simple programming language, and a model of computation (akin to Turing machines and recursive functions).&lt;/p&gt;
&lt;p&gt;It&apos;s composed only of abstractions and applications using variables (e.g. &lt;code&gt;(λx.x) y&lt;/code&gt;). The whole language is made up of &lt;em&gt;lambda terms&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Lambda terms can be one of these three things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;A variable like &lt;code&gt;x&lt;/code&gt; is a valid lambda term.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;An abstraction &lt;code&gt;(λx.y)&lt;/code&gt;, or &lt;code&gt;(λx.t)&lt;/code&gt;, where &lt;code&gt;x&lt;/code&gt; is it&apos;s variable and &lt;code&gt;y&lt;/code&gt;/&lt;code&gt;t&lt;/code&gt; is another lambda term. &lt;code&gt;(λx.x)&lt;/code&gt; could be written in JS as &lt;code&gt;const id = x =&amp;gt; x&lt;/code&gt;. Everything after the &lt;em&gt;dot&lt;/em&gt; works as the body of a function, so you could read &lt;code&gt;λx&lt;/code&gt; as a function receiving &lt;code&gt;x&lt;/code&gt; as variable. That&apos;s why &lt;code&gt;(λx.x)&lt;/code&gt; is what we call identity function, becase it returns what it receives.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;An application &lt;code&gt;(t s)&lt;/code&gt; where both &lt;code&gt;t&lt;/code&gt; and &lt;code&gt;s&lt;/code&gt; are lamba terms.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Since lambda calculus has only functions, we use &lt;a href=&quot;https://en.wikipedia.org/wiki/Church_encoding&quot;&gt;church encoding&lt;/a&gt; to model arithmetic, booleans and data structures by representing data types in the lambda calculus.&lt;/p&gt;
&lt;p&gt;Church numerals let you represent natural numbers under Church encoding:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;0 := &lt;code&gt;λf.λx. x&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;1 := &lt;code&gt;λf.λx. f x&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;2 := &lt;code&gt;λf.λx. f (f x)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;3 := &lt;code&gt;λf.λx. f (f (f x))&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each numeral is a function that takes two arguments, &lt;code&gt;f&lt;/code&gt; and &lt;code&gt;x&lt;/code&gt;, and applies &lt;code&gt;f&lt;/code&gt; &lt;em&gt;n&lt;/em&gt; times to &lt;code&gt;x&lt;/code&gt;. That&apos;s how we know what is each number, by couting how many times &lt;code&gt;f&lt;/code&gt; was applied to &lt;code&gt;x&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It is just a &lt;a href=&quot;https://en.wikipedia.org/wiki/Higher-order_function&quot;&gt;higher-order function&lt;/a&gt;, in Javascript it would look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const zero = f =&amp;gt; x =&amp;gt; x
const one = f =&amp;gt; x =&amp;gt; f(x)
const two = f =&amp;gt; x =&amp;gt; f(f(x))
const three = f =&amp;gt; x =&amp;gt; f(f(f(x)))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you&apos;re not familiar with the notation, you can read it as giving a function &lt;code&gt;f&lt;/code&gt;, and a value &lt;code&gt;x&lt;/code&gt;, apply &lt;code&gt;f&lt;/code&gt; to &lt;code&gt;x&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;A successor function is quite similar. Since two is &lt;code&gt;x&lt;/code&gt; applied to &lt;code&gt;f&lt;/code&gt; two times, to know it&apos;s successor, you should apply &lt;code&gt;f&lt;/code&gt; one more time. So, given a numeral &lt;code&gt;n&lt;/code&gt;, it returns a numeral that applies &lt;code&gt;f&lt;/code&gt; (n+1) times.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;succ := &lt;code&gt;λn.λf.λx.f (n f x)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&apos;s break it into small pieces, because it&apos;s not that easy to understand at the first time.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;1 := λf.λx. f x
succ := λn.λf.λx. f (n f x)

(λn.λf.λx. f (n f x)) (λf.λx. f x)
    (λf.λx. f ((λf.λx. f x) f x)) // now it is a function that expects f and x
    |           (λx. f x) x
    |               f x
    (λf.λx. f (f x)) // it just becomes the definition of two
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By reducing the full expression, we&apos;re now seeing why succ (1 &lt;code&gt;λf.λx. f x&lt;/code&gt;) returns 2 (&lt;code&gt;λf.λx. f (f x)&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;It&apos;s possible to do the same for &lt;a href=&quot;https://en.wikipedia.org/wiki/Lambda_calculus#Arithmetic_in_lambda_calculus&quot;&gt;plus, mult, pow, pred, true, false, and, or, not, ...&lt;/a&gt;. Church encoding can also be used to create a pair (2-tuple), lists with their common functions like head, tail, cons...&lt;/p&gt;
&lt;h2&gt;Evaluation and Reduction&lt;/h2&gt;
&lt;p&gt;The process of computing the value of a lambda term is what we call evaluation or reduction, and we will take a look at the β-reduction.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(λx.x + 1) 5
(5 + 1)
(6)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So we&apos;re applying a function to an argument by replacing the bound variable with that argument.&lt;/p&gt;
&lt;p&gt;A more general rule could be defined as &lt;code&gt;(λx. M) N → M[x := N]&lt;/code&gt;. Where &lt;code&gt;M&lt;/code&gt; is the body of the abstraction, and &lt;code&gt;N&lt;/code&gt;, it&apos;s argument. &lt;code&gt;M[x := N]&lt;/code&gt; replaces all free instances of &lt;code&gt;x&lt;/code&gt;, by &lt;code&gt;N&lt;/code&gt; (I did a bunch of reductions by hand to understand well this process). But this rule requires alpha-conversion to avoid variable capure. It happens when a free variable in &lt;code&gt;N&lt;/code&gt; becomes bound after substitution.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(λx. λy. x) y
     (λy. y)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It changed the meaning of our previous expression. the inner &lt;code&gt;y&lt;/code&gt; in &lt;code&gt;(λy. x)&lt;/code&gt; referred to the outer argument &lt;code&gt;y&lt;/code&gt;. But after substitution, it became bound by the inner &lt;code&gt;λy&lt;/code&gt;. So it no longer refers to the same variable&lt;/p&gt;
&lt;p&gt;With alpha-conversion, we rename bound variables.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(λx. λy. x) y
(λx. λz. x) y
     (λz. y)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We&apos;re giving each scope a unique variable name.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;AND TRUE FALSE
// remember TRUE λx.λy.x and FALSE λx.λy.y

(λp. λq. p q p) TRUE FALSE
     (λq. TRUE q TRUE) FALSE
     TRUE FALSE TRUE
     (λx. λy. x) FALSE TRUE
          (λy. FALSE) TRUE
               FALSE
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Lambda_calculus#Logic_and_predicates&quot;&gt;Try to reduce AND, OR, NOT, IF, etc...&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Starting the interpreter&lt;/h2&gt;
&lt;p&gt;Well, I think what we know until now is enough to at least start writing an interpreter.&lt;/p&gt;
&lt;p&gt;Let&apos;s start defining the AST, which one is just a term:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type term = Var of string | Abs of string * term | App of term * term
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I don&apos;t know if you who is reading this are familiar with OCaml, but it is just the type of a term, &lt;code&gt;... of type&lt;/code&gt; means that a type carries a value, so the type &lt;code&gt;Var&lt;/code&gt; comes with a string value. If you wanna understand more about it, take a read about variant constructors and algebraic data type.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Var of string&lt;/code&gt; in a more practial use:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let x = Var &quot;x&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Same of the others:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let abs = Abs (&quot;x&quot;, Var &quot;x&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you probably have already noticed, this &lt;code&gt;abs&lt;/code&gt; is the same as &lt;code&gt;λx.x&lt;/code&gt;. It&apos;s possible to write a function to do this prettify for us. This function should receive a term, and transform it to string.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let rec to_string ~term =
  match term with
  | Var x -&amp;gt; x
  | Abs (x, body) -&amp;gt; &quot;λ&quot; ^ x ^ to_string ~term:body
  | App (t1, t2) -&amp;gt; to_string ~term:t1 ^ to_string ~term:t2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nothing fancy, just a recursive function calling itself for terms. The &lt;code&gt;^&lt;/code&gt; thing is how to concat strings in OCaml.&lt;/p&gt;
&lt;p&gt;I&apos;m using labeled arguments just because it is a tutorial and I wanna make it easier to read the code. Otherwise I would write it like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let rec to_string t =
  match t with
  | Var x -&amp;gt; x
  | Abs (x, body) -&amp;gt; &quot;λ&quot; ^ x ^ to_string body
  | App (t1, t2) -&amp;gt; to_string t1 ^ to_string t2
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Take a look back at your defined &lt;code&gt;term&lt;/code&gt;. I mentioned the &lt;code&gt;of ...&lt;/code&gt; notation carries a value. We&apos;re using it&apos;s value on the pattern matching: &lt;code&gt;Abs (x, body) -&amp;gt; &quot;λ&quot; ^ x ^ to_string ~term:body&lt;/code&gt;, where &lt;code&gt;x&lt;/code&gt; is the string and &lt;code&gt;body&lt;/code&gt; the term.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Now let&apos;s take a look at the output by writing an anonymous functions to call &lt;code&gt;to_string&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let () =
  let term = App (Abs (&quot;x&quot;, Var &quot;x&quot;), Abs (&quot;y&quot;, Var &quot;y&quot;)) in
  let result = to_string ~term in
  print_endline result
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Output:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt; λxxλyy
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That&apos;s still a bit weird, because it&apos;s missing the &lt;code&gt;.&lt;/code&gt; and &lt;code&gt;()&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let rec to_string ~term =
  match term with
  | Var x -&amp;gt; x
  | Abs (x, body) -&amp;gt; &quot;λ&quot; ^ x ^ &quot;.&quot; ^ to_string ~term:body
  | App (t1, t2) -&amp;gt; &quot;(&quot; ^ to_string ~term:t1 ^ &quot; &quot; ^ to_string ~term:t2 ^ &quot;)&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Output: &lt;code&gt;(λx.x λy.y)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Seems good enough.&lt;/p&gt;
&lt;p&gt;Now we need to reduce expressions. Let&apos;s recap the rule: &lt;code&gt;(λx. M) N  →  M[x := N]&lt;/code&gt;, so, given the body &lt;code&gt;M&lt;/code&gt;, replace all free occurrences of &lt;code&gt;x&lt;/code&gt; with &lt;code&gt;N&lt;/code&gt;. Let&apos;s write it, but in OCaml.&lt;/p&gt;
&lt;p&gt;The signature of what we gonna write: &lt;code&gt;in_term:term -&amp;gt; variable:string -&amp;gt; by_term:term -&amp;gt; term&lt;/code&gt;, so &lt;code&gt;subst ~in_term:M ~variable:&quot;x&quot; ~by_term:N&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let rec subst ~in_term ~variable ~by_term =
  match in_term with
  | Var v -&amp;gt; if v = variable then by_term else Var v
  | App (t1, t2) -&amp;gt;
      App
        ( subst ~in_term:t1 ~variable ~by_term,
          subst ~in_term:t2 ~variable ~by_term )
  | Abs (x, t) -&amp;gt;
      if x = variable then Abs (x, t)
      else Abs (x, subst ~in_term:t ~variable:x ~by_term)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To apply this rule, we also must write a &lt;code&gt;reduce&lt;/code&gt; function, which gonna call &lt;code&gt;subst&lt;/code&gt;, so we gonna substitute terms recursively. Also, worth remembering that we&apos;re doing &lt;a href=&quot;https://en.wikipedia.org/wiki/Lambda_calculus#Reduction_strategies&quot;&gt;normal-order reduction&lt;/a&gt;. It means we gonna reduce from left to right.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let rec reduce ~term =
  match term with
  | App (Abs (var, body), arg) -&amp;gt; subst ~in_term:body ~variable:var ~by_term:arg
  | App (lt, rt) -&amp;gt;
      let lt&apos; = reduce ~term:lt in
      if lt &amp;lt;&amp;gt; lt&apos; then App (lt&apos;, rt) else App (lt, reduce ~term:rt)
  | Abs (var, body) -&amp;gt; Abs (var, reduce ~term:body)
  | Var _ -&amp;gt; term
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Again, idk how much you who are reding this knows about OCaml, so I want to explain a bit this code.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;| App (Abs (var, body), arg)&lt;/code&gt;: we want to check for the tradicional case. It matches for expressions like &lt;code&gt;(λx.x) y&lt;/code&gt;. For this case, we can just call &lt;code&gt;subst&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;| App (lt, rt)&lt;/code&gt;: now we need to handle things like &lt;code&gt;((λx.x)(λy.y))z&lt;/code&gt;. Since we&apos;re implementing a normal-order reduction, we gonna first reduce the &lt;code&gt;lf&lt;/code&gt; (left term). &lt;code&gt;&amp;lt;&amp;gt;&lt;/code&gt; in OCaml compares two structures, so we&apos;re checking if &lt;code&gt;lt&lt;/code&gt; and &lt;code&gt;lt&apos;&lt;/code&gt; differs, if so, we&apos;re returning an application, but with the left-term reduced. Otherwise, the left-term is already reduced, so we reduce the right one.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;| Abs (var, body)&lt;/code&gt;: nothing special, we&apos;re reducing the only available term to reduce.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Good, here&apos;s the code until now:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type term = Var of string | Abs of string * term | App of term * term

let rec to_string ~term =
  match term with
  | Var x -&amp;gt; x
  | Abs (x, body) -&amp;gt; &quot;λ&quot; ^ x ^ &quot;.&quot; ^ to_string ~term:body
  | App (t1, t2) -&amp;gt; &quot;(&quot; ^ to_string ~term:t1 ^ &quot; &quot; ^ to_string ~term:t2 ^ &quot;)&quot;

let rec subst ~in_term ~variable ~by_term =
  match in_term with
  | Var v -&amp;gt; if v = variable then by_term else Var v
  | App (t1, t2) -&amp;gt;
      App
        ( subst ~in_term:t1 ~variable ~by_term,
          subst ~in_term:t2 ~variable ~by_term )
  | Abs (x, t) -&amp;gt;
      if x = variable then Abs (x, t)
      else Abs (x, subst ~in_term:t ~variable:x ~by_term)

let rec reduce ~term =
  match term with
  | App (Abs (var, body), arg) -&amp;gt; subst ~in_term:body ~variable:var ~by_term:arg
  | App (lt, rt) -&amp;gt;
      let lt&apos; = reduce ~term:lt in
      if lt &amp;lt;&amp;gt; lt&apos; then App (lt&apos;, rt) else App (lt, reduce ~term:rt)
  | Abs (var, body) -&amp;gt; Abs (var, reduce ~term:body)
  | Var _ -&amp;gt; term

let () =
  let term = App (Abs (&quot;x&quot;, Var &quot;x&quot;), Var &quot;y&quot;) in
  let result = to_string ~term:(reduce ~term) in
  print_endline result
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It prints to &lt;code&gt;y&lt;/code&gt;, which is fine. But let&apos;s try to reduce this other expression: &lt;code&gt;(λx.λy.x)y&lt;/code&gt; (&lt;code&gt;let term = App (Abs (&quot;x&quot;, Abs (&quot;y&quot;, Var &quot;x&quot;)), Var &quot;y&quot;)&lt;/code&gt;). It prints &lt;code&gt;λy.x&lt;/code&gt;, which is wrong, because we didn&apos;t implemented alpha-conversion yet. We need to fix our &lt;code&gt;subst&lt;/code&gt; &lt;code&gt;Abs (x, t)&lt;/code&gt; match case.&lt;/p&gt;
&lt;p&gt;Let&apos;s write a function to check if a variable is free in a given term.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let rec free_in ~variable ~term =
  match term with
  | Var x -&amp;gt; x = variable
  | App (lt, rt) -&amp;gt; free_in ~variable ~term:lt || free_in ~variable ~term:rt
  | Abs (x, body) -&amp;gt;
      if x = variable then false else free_in ~variable:x ~term:body
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can check if a variable is free, but we still need to rename it somehow, so let&apos;s now write a function to create a new unique name:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let fresh_var =
  let counter = ref 0 in
  fun base -&amp;gt;
    incr counter;
    base ^ string_of_int !counter
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This one is just a closure and we&apos;re creating unique variables by incrementing a counter, and than concating the variable with the counter. In OCaml, this &lt;code&gt;!&lt;/code&gt; means dereference. So we&apos;re getting the ref. value inside counter.&lt;/p&gt;
&lt;p&gt;By refactoring &lt;code&gt;subst&lt;/code&gt;, we should now check for free variables:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let rec subst ~in_term ~variable ~by_term =
  ...
  | Abs (x, t) -&amp;gt;
      if x = variable then Abs (x, t)
      else if free_in ~variable:x ~term:by_term then
        let x&apos; = fresh_var x in
        let t&apos; = subst ~in_term:t ~variable:x ~by_term:(Var x&apos;) in
        Abs (x&apos;, subst ~in_term:t&apos; ~variable ~by_term)
      else Abs (x, subst ~in_term:t ~variable ~by_term)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first &lt;code&gt;if&lt;/code&gt; checks if the variable we want to substitute is the same as the one bound by the lambda.
If they are equal, we don’t perform substitution inside, because within this lambda, that variable name refers to its parameter, not the outer variable we’re replacing.&lt;/p&gt;
&lt;p&gt;Otherwise, we check if the &lt;code&gt;x&lt;/code&gt; appears free in &lt;code&gt;by_term&lt;/code&gt;. If so, we must rename that parameter before substituting.&lt;/p&gt;
&lt;p&gt;Here&apos;s the full code:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type term = Var of string | Abs of string * term | App of term * term

let rec to_string ~term =
  match term with
  | Var x -&amp;gt; x
  | Abs (x, body) -&amp;gt; &quot;λ&quot; ^ x ^ &quot;.&quot; ^ to_string ~term:body
  | App (t1, t2) -&amp;gt; &quot;(&quot; ^ to_string ~term:t1 ^ &quot; &quot; ^ to_string ~term:t2 ^ &quot;)&quot;

let rec free_in ~variable ~term =
  match term with
  | Var x -&amp;gt; x = variable
  | App (lt, rt) -&amp;gt; free_in ~variable ~term:lt || free_in ~variable ~term:rt
  | Abs (x, body) -&amp;gt;
      if x = variable then false else free_in ~variable ~term:body

let fresh_var =
  let counter = ref 0 in
  fun base -&amp;gt;
    incr counter;
    base ^ string_of_int !counter

let rec subst ~in_term ~variable ~by_term =
  match in_term with
  | Var v -&amp;gt; if v = variable then by_term else Var v
  | App (t1, t2) -&amp;gt;
      App
        ( subst ~in_term:t1 ~variable ~by_term,
          subst ~in_term:t2 ~variable ~by_term )
  | Abs (x, t) -&amp;gt;
      if x = variable then Abs (x, t)
      else if free_in ~variable:x ~term:by_term then
        let x&apos; = fresh_var x in
        let t&apos; = subst ~in_term:t ~variable:x ~by_term:(Var x&apos;) in
        Abs (x&apos;, subst ~in_term:t&apos; ~variable ~by_term)
      else Abs (x, subst ~in_term:t ~variable ~by_term)

let rec reduce ~term =
  match term with
  | App (Abs (var, body), arg) -&amp;gt; subst ~in_term:body ~variable:var ~by_term:arg
  | App (lt, rt) -&amp;gt;
      let lt&apos; = reduce ~term:lt in
      if lt &amp;lt;&amp;gt; lt&apos; then App (lt&apos;, rt) else App (lt, reduce ~term:rt)
  | Abs (var, body) -&amp;gt; Abs (var, reduce ~term:body)
  | Var _ -&amp;gt; term

let () =
  let term = App (Abs (&quot;x&quot;, Abs (&quot;y&quot;, Var &quot;x&quot;)), Var &quot;y&quot;) in
  let result = to_string ~term:(reduce ~term) in
  print_endline result

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And it evaluates to &lt;code&gt;λy1.y&lt;/code&gt; now, which is the correct result. The interpreter is already doing beta-reduction and avoiding shadowing by applying alpha-conversion.&lt;/p&gt;
&lt;h2&gt;De Bruijn index&lt;/h2&gt;
&lt;p&gt;Named variables is kinda messy. Renaming variables, generating fresh ones, checking free vars... Even when two functions are structurally the same, if it is using different variables, our interpreter still treats them as different (e.g. &lt;code&gt;λx. λy. x&lt;/code&gt;, &lt;code&gt;λa. λb. a&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;All this process is only used to indicate how far back their binder is.&lt;/p&gt;
&lt;p&gt;De Bruijn indices drops all these names, and replace it with a number, indicating how far back their binder is: &lt;code&gt;λx. λy. x&lt;/code&gt; -&amp;gt; &lt;code&gt;λ. λ. 1&lt;/code&gt; (for the closest binding). Try some exercicies converting from De Bruijn to named, and from named to De Breuijn.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;λ. λ. λ. 3 (2 1)&lt;/code&gt; becomes &lt;code&gt;λx. λy. λz. x (y z)&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;Refactoring&lt;/h3&gt;
&lt;p&gt;The AST has no variables anymore, so we gonna drop all string references:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type term = Var of int | Abs of term | App of term * term
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;let rec to_string ~term =
  match term with
  | Var i -&amp;gt; string_of_int i
  | Abs t -&amp;gt; &quot;λ.&quot; ^ to_string ~term:t
  | App (t1, t2) -&amp;gt; &quot;(&quot; ^ to_string ~term:t1 ^ &quot; &quot; ^ to_string ~term:t2 ^ &quot;)&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When we substitute a term into another, the indices can move depending on how many binders we cross, and that&apos;s why a shift function is needed, to fix the numbers when you relocate a term.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let rec shift ~by ~depth ~term =
  match term with
  | Var i -&amp;gt; if i &amp;gt;= depth then Var (i + by) else Var i
  | Abs t -&amp;gt;
      let depth&apos; = depth + 1 in
      Abs (shift ~by ~depth:depth&apos; ~term:t)
  | App (t1, t2) -&amp;gt; App (shift ~by ~depth ~term:t1, shift ~by ~depth ~term:t2)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;depth&lt;/code&gt; parameter tracks how many binders we&apos;ve gone under. When we find a variable with index &lt;code&gt;i &amp;gt;= depth&lt;/code&gt;, it means this variable refers to something outside our current context, so we need to adjust it by &lt;code&gt;by&lt;/code&gt; amount.&lt;/p&gt;
&lt;p&gt;If we have &lt;code&gt;λ. 0&lt;/code&gt; and want to insert it into another context, we need to shift its free variables so they still point to the right binders.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let rec subst ~index ~arg ~term =
  match term with
  | Var i -&amp;gt; if i = index then arg else Var i
  | Abs t -&amp;gt;
      let index = index + 1 in
      Abs (subst ~index ~arg:(shift ~by:1 ~depth:0 ~term:arg) ~term:t)
  | App (t1, t2) -&amp;gt; App (subst ~index ~arg ~term:t1, subst ~index ~arg ~term:t2)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We&apos;re checking if the variable index matches the one we want to replace. When we go under a lambda, we increment the index we&apos;re looking for, and shift the argument up by 1 so its free variables still refer to the correct binders.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let rec reduce ~term =
  match term with
  | App (Abs body, arg) -&amp;gt;
      shift ~by:(-1) ~depth:0
        ~term:(subst ~index:0 ~arg:(shift ~by:1 ~depth:0 ~term:arg) ~term:body)
  | App (t1, t2) -&amp;gt;
      let t1&apos; = reduce ~term:t1 in
      if t1 &amp;lt;&amp;gt; t1&apos; then App (t1&apos;, t2) else App (t1, reduce ~term:t2)
  | Abs t -&amp;gt; Abs (reduce ~term:t)
  | _ -&amp;gt; term
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;About the case &lt;code&gt;App (Abs body, arg)&lt;/code&gt;:&lt;/p&gt;

&lt;li&gt;
&lt;p&gt;First we shift the argument up by 1: &lt;code&gt;shift ~by:1 ~depth:0 ~term:arg&lt;/code&gt;. It is about to place it inside the lambda body, where all free variables are one level deeper&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Then substitute it at index 0: &lt;code&gt;subst ~index:0 ~arg:... ~term:body&lt;/code&gt;. Index 0 refers to the immediately bound variable.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Then shift down by -1: &lt;code&gt;shift ~by:(-1) ~depth:0&lt;/code&gt;. After substitution, we&apos;re removing one lambda, so we need to decrease all free variable indices.&lt;/p&gt;
&lt;/li&gt;

&lt;p&gt;The rest is similar to the named version, we&apos;re reducing from left to right (normal-order), and reducing inside abstractions.&lt;/p&gt;
&lt;p&gt;To fully normalize a term:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let rec normalize ~term =
  let t = reduce ~term in
  if term = t then term else normalize ~term:t
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This keeps reducing until the term doesn&apos;t change anymore&lt;/p&gt;
&lt;h3&gt;Testing with Church numerals&lt;/h3&gt;
&lt;p&gt;Now let&apos;s test our interpreter with Church numerals.
Two is &lt;code&gt;λf.λx. f (f x)&lt;/code&gt;, which in De Bruijn notation becomes &lt;code&gt;λ. λ. 1 (1 0)&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let two = Abs (Abs (App (Var 1, App (Var 1, Var 0))))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And the successor function &lt;code&gt;λn.λf.λx. f (n f x)&lt;/code&gt; becomes &lt;code&gt;λ. λ. λ. 1 (2 1 0)&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let succ = Abs (Abs (Abs (App (Var 1, App (App (Var 2, Var 1), Var 0)))))
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;let () =
  let two = Abs (Abs (App (Var 1, App (Var 1, Var 0)))) in
  let succ = Abs (Abs (Abs (App (Var 1, App (App (Var 2, Var 1), Var 0))))) in
  print_endline (to_string ~term:(normalize ~term:(App (succ, two))))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Output: &lt;code&gt;λ.λ.(1 (1 (1 0)))&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This is the Church numeral for 3, which is &lt;code&gt;λf.λx. f (f (f x))&lt;/code&gt;. It applies &lt;code&gt;f&lt;/code&gt; three times to &lt;code&gt;x&lt;/code&gt;&lt;/p&gt;
</content:encoded></item></channel></rss>