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_PARENThen, 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).
| AST Constructor Signature | Example |
|---|---|
|
|