A cheatsheet for Hack's high level language




About

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



General

Case sensitive

Comma delimited

File extension is .jack



Comments

Single line comments start with //

Multiple line comments start with /* and end with */



Valid Names

A valid name must begin with a letter or underscore. The remaining characters can be digits, letters, or underscores



Numbers

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



Operations

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


Data Types

Standard

int Integer

For details see Numbers

char Character

Printable ASCII characters

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



Variable Types

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 ) );
	}
}


Variable Declaration

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();
		...
	}
}


Subroutine Types

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



Subroutine Declaration

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



Class Declaration

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


Statements

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;
}


Program Structure

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



File Includes

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



Memory Management

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 )'


Miscellaneous

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



Compatibility

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



Thanks

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