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:
<- boolean / ID / NUMBER
scalar <- call / scalar / LEFT_PAREN expression RIGHT_PAREN atom
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).
AST Constructor Signature | Example |
---|---|
|
|