This cheatsheet assumes that you are using this compiler. However, most of the information is also applicable to compilers that follow the specifications of TECS
Use the navigation bar to jump to a section of interest
Case sensitive
Comma delimited
File extension is .jack
Single line comments start with //
Multiple line comments start with /*
and end with */
A valid name must begin with a letter or underscore. The remaining characters can be digits, letters, or underscores
Positive integers
Positive integers range from 0 to 32767 (inclusive)
They can be expressed as decimal, hexadecimal, binary, or ASCII characters. For example, the following set a
to the same value:
// These are all equivalent.
// They will be stored as the same 16-bit value in memory.
var int a;
a = 36; // decimal
a = 0x24; // hexadecimal
a = 0x0024; // hexadecimal (leading zeros are ignored)
a = 0b100100; // binary
a = 0b00100100; // binary (leading zeros are ignored)
a = '$'; // ASCII character
Note:
Hexadecimal and binary representations are case insensitive (0xFFFF
is equivalent to 0xffff
and 0XffFF
)
ASCII characters can only be used to represent integers in the range 32 to 126 (inclusive). These correspond to the printable ASCII characters
Negative integers
Negative integers range from -1 to -32768 (inclusive)
To express a negative integer, simply precede a positive integer with the negation symbol, -
var int a;
a = - 36; // decimal
a = - 0x24; // hexadecimal
a = - 0b100100; // binary
How does one express -32768?
Since 32768 falls outside the positive integer range, we have to get clever:
var int a;
a = - 32767 - 1; // equivalent to -32768
We can also use the fact that integers in Hack are 16-bit and interpreted as signed:
var int a;
a = 32767 + 1; // (1) equivalent to -32768
a = ! 32767; // (2) equivalent to -32768
The first approach works because 32768 equals -32768!
But how you ask, that makes absolutely no sense?! This quirk is a result of interpreting integers as signed using two's complement notation
The second approach works by flipping bits:
32767 is equivalent to 0b0111111111111111
32768 is equivalent to 0b1000000000000000
Fractions
At the moment, the compiler does not support non-integer numbers. This may change in the future
Arithmetic
+
Add
-
Subtract
*
Multiply
/
Divide
Comparison
==
Equal to
!=
Not equal to
<
Less than
>
Greater than
<=
Less than or equal
>=
Greater than or equal
Bitwise
!
Not
&
And
|
Or
^
Xor
<<
Logical shift left
>>
Logical shift right
Caveats
The compiler does not support operator precedence. Use parentheses to specify a desired order. For example:
var int a, b;
a = 3 - 5 * 4; // evaluates to -8
b = 3 - ( 5 * 4 ); // evaluates to -17
Standard
int
Integer
For details see Numbers
char
Character
Enclosed in single quotes
For example:
var char a;
a = '@';
bool
Boolean
true
or false
false
is equivalent to 0
true
is equivalent to -1
. However, any non-zero integer is also treated as equivalent to true
(most of the time)
Class Instances
An instance of a class has the class as its data type
For example:
var String a; // String data type
var String b; // String data type
var Array c; // Array data type
a = "pizza"; // a is an instance of the String class
b = String.new( 7 ); // b is an instance of the String class
c = Array.new( 5 ); // c is an instance of the Array class
Caveats
There is no type checking. For example, you will not get an error from the following code:
var int i;
var Fruit f;
var Array a;
i = 99 + "cats"; // compiler does not type check before performing addition
f = Fruit.pickRandom();
a = Array.new( f ); // compiler does not type check arguments. In this case,
// Array.new() is expecting an argument of integer type,
// but receives one of Fruit type.
If you are curious:
i
is assigned the value of 99 + the base address of the String instance
a
is assigned the base address of a new Array instance. The Array instance has a size equal to the base address of the Fruit instance f
var
Subroutine scope
Local to the subroutine it is declared in
static
Class scope
Available to all instances (members) of the class
field
Class scope
Unique to each instance of the class
Allocated space in memory (heap) only when an instance is created
const
Can be either class or subroutine in scope
Value is assigned at declaration and cannot be subsequently changed
Does not use memory (heap)
Limited to int
and char
data types
For example:
const int a = 120;
const int b = - 78;
const int c = ! 89;
const char d = '$';
const int e = 0b1001;
// const int f = 20 + 30; // not supported
// Only the unary operators '-' and '!' are supported
Here's an example of the various variable type in use:
class Point {
const int max = 9000; // shared among all instances of the Point class
static int pointCount; // shared among all instances of the Point class
field int x, y; // each instance gets its own x and y variables
constructor Point new ( int ax, int ay ) {
x = ax; // the instance specific x is updated
y = ay; // the instance specific y is updated
pointCount += 1; // the global pointCount is updated
if ( pointCount > max ) {
Sys.raiseException( "Too many points!" );
}
return this;
}
method int distance ( Point b ) {
var int dx, dy; // variables dx and dy exist only within this subroutine
dx = x - b.getX();
dy = y - b.getY();
return Math.sqrt( ( dx * dx ) + ( dy * dy ) );
}
}
Syntax is variableType dataType name
See variable types
See data types
See valid names
Where to declare:
static
and field
at the beginning of the class
var
at the beginning of a subroutine
const
at the beginning of the class or a subroutine
At the moment, the compiler does not support simultaneous declaration and assignment for variable-types static
, field
, and var
Here are some examples of variable declarations:
class Point {
const int max = 9000;
static int pointCount;
field int x, y;
method int distance ( Point b ) {
var int dx, dy;
dx = x - b.getX();
...
}
}
constructor
Creates a new instance of the class
Returns a pointer to the new instance (i.e. this
)
Call syntax is className.new()
method
Can directly use variables and methods that belong to the current instance
Typically used to access or modify the current instance's variables
Call syntax is instanceName.methodName()
Call syntax when referring to self is methodName()
function
Cannot directly use variables and methods that belong to the current instance
Typically used to accomplish class level tasks
Call syntax is className.functionName()
Here's an example of the various subroutine types in use:
class Point {
static int pointCount;
field int x, y;
constructor Point new ( int ax, int ay ) { // creates a new instance of the Point class
x = ax;
y = ay;
pointCount += 1;
return this; // returns the base address of the created instance
// (i.e. returns a pointer to the instance)
}
method int getX () { // uses the current instance's x variable
return x;
}
method int getY () { ... } // uses the current instance's y variable
method int distance ( Point b ) { // uses the current instance's x and y variables
var int dx, dy;
dx = x - b.getX(); // calls a method of the Point class, getX()
dy = y - b.getY(); // calls a method of the Point class, getY()
return Math.sqrt( ( dx * dx ) + ( dy * dy ) );
}
function void printStatus () { // does not use anything specific to the current instance
GFX.printInt( pointCount );
GFX.printString( " points have been created" );
GFX.println();
}
function int distanceDeux ( Point a, Point b ) { // does not directly use anything specific to the current instance
var int dx, dy;
dx = a.getX() - b.getX(); // calls a method of the Point class, getX()
dy = a.getX() - b.getY(); // calls a method of the Point class, getY()
return Math.sqrt( ( dx * dx ) + ( dy * dy ) );
}
}
class Main {
function void main () {
var Point p1, p2;
p1 = Point.new( 1, 2 ); // creates a new instance of Point class
p2 = Point.new( 3, 4 ); // creates a new instance of Point class
Point.printStatus(); // calls a function of the Point class
}
}
Note:
A function
can use the current instance's variables and methods, but this must be done indirectly. For example, this can be achieved above by passing the this
keyword as one of the arguments to distanceDeux
. However, a method (such as distance
) is a cleaner (and preferred) way to get the same effect
Syntax is:
subroutineType dataTypeOfReturnValue name () {
// code
return returnValue;
}
subroutineType dataTypeOfReturnValue name ( dataTypeOfParameter parameterName, dataTypeOfParameter parameterName, ... ) {
// code
return returnValue;
}
See subroutine types
If the subroutine does not return a value, use the keyword void
for dataTypeOfReturnValue. Otherwise see data types
See valid names
Syntax is:
// class name should be in title case
class ClassName {
// class level variables must be declared before subroutines
const dataType constName = constValue; // constants must be declared before static and field variables
static dataType staticName;
field dataType fieldName;
constructor className constructorName ( parameters ) {
var dataType varName;
...
return this;
}
method dataTypeOfReturnValue methodName ( parameters ) {
var dataType varName;
...
return returnValue;
}
function dataTypeOfReturnValue functionName ( parameters ) {
var dataType varName;
...
return returnValue;
}
}
Everything inside a class is optional
class EmptyClass {} // This is valid
If (Else)
Syntax is:
if ( condition ) {
...
}
else if { // optional block
...
}
else { // optional block
...
}
While Loop
Syntax is:
while ( condition ) {
...
}
For Loop
Syntax is:
for ( initialization; condition; update ) {
...
}
The variable used in the iteration must be declared beforehand
For example:
var int i;
for ( i = 0; i < 15; i += 2 ) {
GFX.printInt( i );
}
Break And Continue
Used in for and while loops
break
exits the current loop
For example:
while ( true ) {
if ( n == 50 ) {
break; // if we never reach this break, the loop will run forever
}
n += 1;
}
continue
exits the current iteration
For example:
var int n;
n = 10;
while ( n > 0 ) {
if ( n % 2 == 0 ) {
continue; // skip even numbers
}
GFX.printInt( n ); // print the odd numbers (9,7,5,3,1)
n -= 1;
}
Each program must have a class named Main
containing a function named main
. This acts as the entry point into the program. When asked to run a program, the OS will call the program's Main.main()
function
A program can consist of a single class (Main
) or multiple classes
Each of these classes resides in its own file
Each of these files reside in the same directory (a.k.a. the program directory)
Directory Structure
Each program should have its own directory
All .jack files present in the directory are considered (by the compiler) to be part of the program
A program can use files that are not in its directory via include statements
The directory's name is the program's name. It can be whatever valid name you want
For example, here is the directory structure for the program called GASscroller:
GASscroller | |— GASscroller.jack | |— Main.jack | |— MathsToo.jack | |— MemoryToo.jack | |— Sinus.jack
File Structure
One file per class
The file name must be the same as the class name. This includes capitalizations
Useful when want to use a file in another* directory
Syntax is:
include "someDir/someFile.jack"
include "../someDir/someFile.jack"
include "C:/someDir/someDir/someFile.vm"
class ClassName {
...
}
Paths:
can be relative or absolute
are enclosed in double quotes
use forward slashes /
Files can be of type .jack or .vm
*Files in the program directory are automatically included. So are standard library files
When writing your code, you should ensure that you always dispose (deallocate) any unused objects. Otherwise you will find yourself running short on memory, as there is no automatic garbage collection
For example:
/* Let's say you make a string */
var String s;
s = "A brand new day"; // a chunk of memory has been allocated to
// hold the string "A brand new day"
// via a background call to 'String.new( 15 )'
/* And that later on in your code, you want to give it a different value */
/* Do not do this: */
s = "A day full of possibilities"; // a chunk of memory has been allocated
// to hold the string "A day full of possibilities"
// via a background call to 'String.new( 27 )'
/* Why?
The memory block where "A brand new day" is stored is still allocated.
Therefore it is not available for use. Worse yet, we no longer have a
way to free that memory block. When we assigned 's' a new value, we also
severed its connection to that memory block.
*/
/* What to do: */
// First free the chunk of memory holding "A brand new day"
s.dispose();
// Then give 's' the new value
s = "A day full of possibilities"; // a chunk of memory has been allocated
// to hold the string "A day full of possibilities"
// via a background call to 'String.new( 27 )'
The compiler (at the moment) does not check whether a class has a given subroutine. Compilation will proceed as usual, but the resulting program will have undefined behaviour. If you are lucky, the program will fail visibly. For example:
var Array a;
a.nonExistantMethod(); // no error raised during compilation
String.anotherNonExistantMethod(); // no error raised during compilation
The Hack computer has no memory protection. A program can access any register in memory
The official TECS syntax is supported by the compiler. That is, you can compile programs that use:
let
and do
keywords
=
for equality comparison
~
for not
The VM and Assembly code generated by the compiler are not compatible with the official TECS emulators
Many of the changes that make the language more pleasant to use are based on detailed forum posts by Mark Armbrust:
Making the let
and do
keywords optional ↗
Hexadecimal and character integer types ↗
Break and continue ↗
Constant variable types ↗
Support for else if
↗