Public Domain JACK compiler
This public domain software is a sustainable economy project.
The Unofficial Jack Programming language
Abstract
The
Jack programming language1 is described in the book
The Elements of Computing Systems8 and on the website
www.nand2tetris.org7. There is a
set of video3 on Youtube talking about it.
This document is describing a Jack compiler written in Jack which
outputs C code.
At the beginning it was itself bootstrapped with a simple Python3 Jack compiler.
Status of this document
This document is released into the public domain.
Table of Contents
1.
Introduction[1]
1.
Background[2]
2.
Differences from the book[3]
2.
Differences from VMEmulator[4]
2.
Hello World![5]
3.
Specification[6]
4.
Classes[7]
5.
Grammar[8]
1. Introduction
Elon Musk asks "is
life a video game9" ?
In a blues song (the roots of Hard Rock), AC/DC says "she's got
the Jack4" and "she's a dirty woman".
Could the roots of our Universe be coded in Jack programming language by our ancestors ?
Jack is easy to learn but it is designed to be yet useful.
1.1 Background
The Jack language was design for teaching the basis of computer construction.
1.2 Differences from the book
Differences from the original Jack specification are :
The range of decimal constant number and integer variable is unspecified.
The size of the integer is at least the same size as the size of a memory
address. (It could be from 8 to 512 bits).
The callback extension allows to call a `callback` method
from a Callback object.
The inline assembly comment extension allows to insert target language code
between `//#` and `/*# #*/` comments.
There is additional classes to allow interaction with the operating
system (files and directories...).
1.3 Differences from VMEmulator
With the `-hack` option the public domain jack compiler is
disabling these differences.
Differences from the official first edition of Hack Java VMEmulator are :
The `\` in strings literal does not escape `\` and `"`.
The official JackCompiler does.
The `while` and `if` conditions are coherent. In the official JackCompiler the
`while`
condition is true if it is equal to -110, but the
`if` condition is true if it is not equal to 0.
The public domain jack compiler treats both conditions like the `if`.
The static and local variables are not initialized to 0 at start up.
The VMEmulator seems to do it.
2. Hello world
Source code :
class Main {
function void main()
{
do Output.printString("Hello World!");
do Output.println();
return;
}
}
With Debian or Ubuntu, you need to install a valid OpenGL development environment, using:
sudo apt install build-essential git libx11-dev libgl1-mesa-dev libglu1-mesa-dev
Then clone this repository :
git clone https://github.com/public-domain/jack.git
Build the compiler :
cd jack; make
Create an empty directory structure using
mkdir -p hello/lib
Save the text source code in a file named `hello/Main.jack` using your
favorite plain text editor.
Copy the JackOS and the C run-time from the
public domain jack compiler source code :
cp -r lib/std/ hello/lib/std; cp -r lib/c/ hello/lib/c/
Translate it to C using
./jack.run -hack hello
Make an executable using
cc -o hello.run hello.c -lX11 -lGL -lGLU
Run it
./hello.run
Done!
3. Specification
3.1 Structure of a program
A Jack program is composed of one or more files that each describe a single
class. Each class is compiled separately then they are all linked to create
a single executable file.
A class name begins with a capital letter and is saved in a text file with
the same name with the `.jack` extension appended.
All the `.jack` files for a project are located in a single source
directory, possibly with sub-directories.
A class name, variable name or subroutine name (identifiers) always begin with
a letter or a `_` and next is composed of letters, `_` or digits.
The identifiers are case sensitive.
`Name.jack` :
/** class definition */
class Name { // Name is the name of the class
static int static_variable_name; // static or field declarations
field int field_variable_name; // in any order, type or quantity
static char static_variable_name2;
field char field_variable_name2;
static boolean static_variable_name3;
field boolean field_variable_name3;
static Name static_variable_name4;
field Name field_variable_name4;
// ...
/* subroutines declarations */
// method, constructor or function in any order and quantity
// a constructor always returns an object of type of its class.
// subroutines can have any quantity of parametres.
constuctor Name constructorName(int parameter1, String parameter2) {
var int local_variable_name; // local variable in any type
var Array local_variable_name2; // or quantity
// ...
// statements
let local_variable_name2 = null;
return this;
}
// a function doesn't have the `this` object
function int functionName(char parameter1) {
var int local_variable_name;
var Name object;
var boolean condition;
// ...
// statements
let condition = false;
if (~condition) {
let local_variable_name = 1 + 2;
} else {
let local_variable_name = 1 * 2;
}
let object = Name.constructorName(1, "Hello");
do object.methodName(true, Array.new(3));
do object.dispose();
return 0;
}
// a method has an object `this` of its class
method void methodName(boolean parameter1, Array parameter2) {
var int local_variable_name;
var boolean condition, local_var_2, local_var_3;
var Name an_object;
// ...
// statements
let an_object = this;
let condition = true;
let local_variable_name = 3 & 1;
while (condition) {
do Name.functionName(65);
do methodName(false, null);
let parameter2[0] = 7 - 1;
if (local_variable_name > 0) {
let parameter2[1] = 7 - (3 / 2);
}
if (local_variable_name > 0) {
let parameter2[1] = 1 | 2;
}
if (local_variable_name = 0) {
return;
}
let condition = ~condition;
}
return;
}
}
3.2 Variables
`static` kind variables are living during all the program execution and
have a class scope.
`field` kind variables have the life of an object have an object scope.
`var` kind variables and parameter variables have the life of
subroutine execution and a scope of the local subroutine.
Every kind of variable is accessed only by its identifier. You don not
need to add class identifier for static variable. And you cannot
access fields of object. If you need to access field, you need to
write getter and setter.
Each variable has a type that can be a primitive or a object type of a class.
The primitives types are `int` 2's complement integer, `boolean` that can be
`true` or `false`, and `char` an UTF-16 Unicode character.
A constant can be assigned to a variable.
Integer constant are always positive.
The unary minus `-` is used to get negative numbers.
There is only decimal constant. No hex, octal or character constants.
On 16bit platforms the biggest integer constant is 32767, on 32bit
2147483647, on 64bit 9223372036854775807.
To get the most negative number you must use these biggest constants
and add `1` to them.
String constant are beginning and ending with `"` and can't contain `"` or
newline (ascii character 10 in decimal). There is no escape
sequences (`\n \\ \t \"` are not producing the same result as in other
programming languages).
The `null` constant is a null reference to an object
(equivalent to `0` or `false`).
Boolean `true` is `-1` and `false` is `0`.
Variables are weakly typed you can assign any type of variable to any
type of other.
That code is perfectly valid:
var int variable;
var Array arr;
let variable = 2000;
let arr = variable;
let arr[1] = 3; // now memory located at address 2001 contains the value 3
3.3 Subroutines
`constructor` are automatically allocating the memory for an object of type
of their class. Inside the constructor `this` is a reference to the
current newly created object. A constructor must end with `return this;`
statement and its return type must be the one of its class.
Constructors are called using the
syntax.
The `dispose()` method is generally used in counterpart of constructors to
free memory used by the object.
function` subroutines are called using the
syntax.
`method` subroutines are called using the
Methods have the `this` reference to the current object of type of their class.
The last statement of subroutines is always a `return` statement.
Note that the compiler has no way to find the return type of a subroutine
which is not inside its class scope. So in fact `void` subroutines return
`0`.
3.4 Statements
`let` syntax is
`do` syntax is
`if` syntax is
if (expression) {
statements; // executed when expression evaluates to true
} else {
statements; // executed when expression evaluates to false
}
The `else { statements; }` is an optional clause.
`while` syntax is
while (expression) {
statements; // executed in loop until expression evaluates to false
}
`return` syntax is
or ```return;``` for `void` subroutines.
### 3.5 Expressions
An expression can be:
- A constant (`1234` or `"Hello World"` or `true`/`false` or `null`)
- A variable name
- The `this` keyword
- A `variable_name[expression]`
- A subroutine call
- A `~expression` // bit-wise Boolean INVERT on int and logical NOT on others
- A `-expression` // unary minus
- A `(expression)` // expression enclosed in parenthesis
- Or :
expression + expression // integer addition
expression - expression // integer 2's complement subtraction
expression * expression // integer multiplication
expression / expression // integer division
expression & expression // bit-wise AND on int and logical AND on others
expression | expression // bit-wise OR on int and logical OR on others
expression > expression // is greater than
expression = expression // is equal
expression < expression // is less than
There is no priority of operators. You must always enclose sub-expressions
in parenthesis. Result of `let variable = 2 * 4 + 5;` is undetermined,
you must use `let variable = (2 * 4) + 5;`.
### 3.6 Comments
//` is introducing a single line comment, the text after `//` is
ignored till the end of line.
/*` is introducing a multi line is comment, the text after `/*` is
ignored till `*/` is encountered.
Special `/**` is introducing an API documentation comment.
Extension `/*#` is introducing an inline assembly source code and runs till
#*/` is encountered. `/*#` is followed by one to four characters that
indicate the type of target assembly language.
Extension `//#` acts the same as `/*#` except it is single line.
WARNING: These extensions are only supported by the
public domain jack compiler described in this document.
### 3.7 Callback extension
The `callback` keyword extension allows to call a method referenced
by an object. It is useful for creating generic algorithms independent
of types.
WARNING: This extension is only supported by the
public domain jack compiler described in this document.
Do not use it for the original Hack platform.
This extension is a cheap replacement for `virtual` C++ functions and
Java non-static, non-final, non-private methods.
The syntax is:
class MyCallback {
// the class must begin with a `int` field named "callback"
field int callback;
// There can be only one callback per class.
// the callback is called by invoke()
// it must return int and have two int parameters
method int callback(int a, int b) {
// do something with a and b.
// a and b can be assigned to variable of a class type
// to do something useful by calling then a method.
var Array array;
let array = a;
if (array) {
do array.dispose();
}
return 0;
}
constructor MyCallback new() {
// the compiler automatically assigns the callback method to the
// callback field of the object
return this;
}
method void dispose() {
do Memory.deAlloc(this);
return;
}
method int invoke(int a, int b) {
return callback(a, b); // the magic is here.
// it calls the callback referred
// by the callback field in
// the `this` reference
}
}
class MyOtherCallback {
field int callback;
constructor MyOtherCallback new() {
return this;
}
method int callback(int a, int b) {
return 0; // do nothing
}
// we dont need the invoke and dispose methods since we
// never use them in this class
}
class Main {
function void Main() {
var MyCallback cb;
let cb = MyCallback.new();
do cb.invoke(0, 0); // calls MyCallback.callback()
do cb.dispose();
let cb = MyOtherCallback.new();
do cb.invoke(0, 0); // calls MyOtherCallback.callback()
// without the extension it would have
// called MyCallback.callback() again
do cb.dispose();
return;
}
}
## 4. Classes
### 4.1. Math
See the [book][8].
### 4.2. String
See the [book][8].
### 4.3. Array
See the [book][8].
### 4.4. Output
See the [book][8].
### 4.5. Screen
Screen resolution is 512 x 256 pixels.
The screen memory starts at memory location 16384 and ends at 24575.
Pixels are single bit black and white packed in 16bit words.
There is 32 16bit words by line of screen.
See the [book][8] for more.
### 4.6. Keyboard
The current pressed key Unicode value is located at memory address 24576.
There is special key values:
- Newline is 128.
- Backspace is 129
- left arrow is 130
- up arrow is 131
- right arrow is 132
- down arrow is 133
...
See the [book][8] for more.
### 4.7. Memory
See the [book][8].
### 4.8. Sys
See the [book][8].
### 4.9. File
Manage files and directories.
constructor File new(String path, boolean writing)
path: path of the file or directory.
writing: true if you want write to the file.
return: an object of type File.
method String getName()
return: the path.
method boolean isdir()
return: true if path is a directory.
method boolean open()
return: true if the file has been successfuly opened.
method int readByte()
return: a single byte read from the file or -1 if the
end of file has been reached.
method String readLine(String buffer)
return: a line from the file or `null` if end of file has
been reached.
method int seek(int position)
Move the current byte position in the file
for writing or reading.
return: position if successful.
method int writeByte(int data)
return: 1 if a single byte "data" has been writen to the file.
method int writeString(String s)
return: the length of the "s" String successfuly writen to
the file.
method boolean remove()
return: true if the file or directory has been deleted.
method boolean mkdir()
return: true if the directory has been created successfuly.
method Buffer list()
return: a Buffer containing the list of files or directories
in the "path" directory.
Usage :
var Buffer dir;
var File file;
var int i, c;
var String d;
let file = File.new("./", false);
let dir = File.list();
if (~dir) { return; }
let i = 0;
while (i < dir.getSize()) {
let d = dir.getAt(i);
do Output.printString(d);
do Output.println();
do d.dispose();
let i = i + 1;
}
do dir.dispose();
do file.dispose();
let file = File.new("hello.txt", true);
do file.writeString("Hello World!");
do file.writeByte(10); // newline
do file.dispose();
let file = File.new("hello.txt", false);
let c = file.readByte();
while (c > 0) {
do Output.printChar(c);
let c = file.readByte();
}
do file.dispose();
let file = File.new("hello.txt", false);
do file.remove();
do file.dispose();
### 4.10. Buffer
An Array that grows automatically.
constructor Buffer new(int initial, Callback dispose_cb)
initial: preallocated size of the internal Array.
dispose_cb: a Callback to free elements when the Buffer is
disposed or "null" if the elements don't need
to be unallocated.
return: a Buffer object with preallocated "initial"
elements space.
method Array getAt(int index)
return : the element at postion "index".
method int append(Array data)
return: the index of the element "data" appended to the
internal Array.
method int getSize()
return : the number of elements in the buffer.
## 5. Grammar
keyword: one of
class constructor function method callback field static var int char boolean void true false null this let do if else while return
symbol: one of
{ } ( ) [ ] . , ; + - * / & | < > = ~
digit: one of
0 1 2 3 4 5 6 7 8 9
letter: one of
a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
integerConstant:
digit digit_opt
stringConstant:
" unicodeExceptNewlineAndDoublequote_opt "
letterOrUnderscore:
_
letter
digitOrLetterOrUnderscore:
letterOrUnderscore
digit
identifier:
letterOrUnderscore digitOrLetterOrUnderscore_opt
classDec:
class className { classVarDecList_opt subroutineDecList_opt }
varNameList:
varName
varNameList, varName
classVarKind:
static
field
classVarDecList:
classVarDec
classVarDecList classVarDec
classVarDec:
classVarKind type varNameList ;
field int callback ;
type:
int
char
boolean
className
subroutineKind:
constructor
function
method
subroutineType:
type
void
subroutineDecList:
subroutineDec
subroutineDecList subroutineDec
subroutineDec:
subroutineKind subroutineType subroutineName ( parameterList_opt ) subroutineBody
parameterList:
type varName
parameterList, type varName
subroutineBody:
{ varDecList_opt statements }
varDecList:
varDec
varDecList varDec
varDec:
var type varNameList ;
varNameList:
varName
varNameList , varName
className:
identifier
subroutineName:
identifier
callback
varName:
identifier
statements:
statement
statements statement
statement:
letStatement
ifStatement
whileStatement
doSatement
returnStatement
letStatement:
let varName indexExpression_opt = expression ;
indexExpression:
[ expression ]
ifStatement:
if ( expression ) { statements } elseStatement_opt
elseStatement:
else { statements }
whileStatement:
while ( expression ) { statements }
doStatement:
do subroutineCall ;
returnStatement:
return expression_opt;
return callbackCall;
expression:
term
expression op term
term:
integerConstant
stringConstant
keywordConstant
varName indexExpression_opt
subroutineCall
( expression )
unaryOp term
varOrClassName:
className
varName
subroutineCall:
subroutineName ( expressionList_opt )
varOrClassName . subroutineName ( expressionList_opt )
callbackCall:
callback ( expression , expression )
expressionList:
expression
expressionList , expression
op: one of
+ - * / & | < > =
unaryOp: one of
- ~
keywordConstant:
true
false
null
this
***
[1]: https://www.nand2tetris.org/project09
[2]: https://www.csie.ntu.edu.tw/~cyy/courses/introCS/13fall/lectures/handouts/lec11_Jack.pdf
[3]: https://www.youtube.com/watch?v=KBTg0ju4rxM&list=PLrDd_kMiAuNmllp9vuPqCuttC1XL9VyVh
[4]: https://www.youtube.com/watch?v=kq_GSIw0X0w
[5]: https://commons.wikimedia.org/wiki/File:UN_emblem_blue.svg
[6]: https://tekeye.uk/playing_cards/svg-playing-cards
[7]: https://www.nand2tetris.org/
[8]: https://www.amazon.com/Elements-Computing-Systems-Building-Principles/dp/0262640686/ref=ed_oe_p
[9]: https://www.youtube.com/watch?v=2KK_kzrJPS8
[10]: http://nand2tetris-questions-and-answers-forum.32033.n3.nabble.com/Supplied-compiler-doesn-t-agree-what-is-true-between-if-statement-and-while-statement-td4032492.html
[11]: https://www.sustainable.org/economy/
[12]: https://web.archive.org/web/20110920035144/http://www.dcd.pl/asoft.php
[13]: https://archive.org/details/bitsavers_motorola
[14]: https://unlicense.org/
[15]: https://www.sqlite.org/copyright.html
[16]: https://ohwr.org/cern_ohl_p_v2.txt
[17]: https://www.youtube.com/watch?v=Q8RqgVjIDB8
[18]: https://www.economist.com/big-mac-index
[19]: https://www.travailler-en-suisse.ch/tendances-salaires-informaticiens-suisse.html
[20]: https://opensource.org/licenses
[21]: http://www.salaryexplorer.com/salary-survey.php?loc=210&loctype=1&job=1&jobtype=1
***