Compiling to Assembly
from Scratch

Table of Contents


Chapter 10
Primitive Scalar Data Types

When we say scalar data types we mean data types that fit into a single machine word, which is not a pointer but carries a value in itself. We already have one scalar data type for integer numbers.

Let’s introduce a boolean data type. First, we need an AST node for it:

class Boolean implements AST {
  constructor(public value: boolean) {}

  emit(env: Environment) {
    if (this.value) {
      emit(`  mov r0, #1`);
    } else {
      emit(`  mov r0, #0`);
    }
  }

  equals(other: AST): boolean {…}
}

We emit it the same way as integers 1 and 0 (for true and false).

In the parser, we introduce new tokens for true and false, and compose them to create a boolean parser:

let TRUE =
  token(/true\b/y).map((_) => new Boolean(true));
let FALSE =
  token(/false\b/y).map((_) => new Boolean(false));

let boolean: Parser<AST> = TRUE.or(FALSE)

We can extend the atom rule of the expression grammar by adding a boolean alternative. A good idea at this point is to introduce an additional scalar rule:

scalar <- boolean / ID / NUMBER
atom <- call / scalar / LEFT_PAREN expression RIGHT_PAREN

Then, after implementing this grammar as a parser we get booleans in our extended baseline language:

assert(true);
assert(!false);

However, they behave exactly like integers 1 and 0, so assert(true == 1) will succeed. Under static typing, this comparison would be rejected by the compiler. Under dynamic typing, this would evaluate to false at run-time.


Similarly, we can add other scalars, such as undefined, null (that compile to 0, like false), or a character type, which could compile to the integer value of its ASCII code (though, JavaScript and TypeScript treat characters as strings).

Summary of AST constructor signatures with examples
AST Constructor Signature Example

Boolean(value: boolean)

Undefined()

Null()

true

undefined

null

Next: Chapter 11. Arrays and Heap Allocation