<<

IWKS 3300: NAND to Tetris Spring 2019 John K. Bennett

High Level Language (Jack)

Foundations of Global Networked Computing: Building a Modern Computer From First Principles

This course is based upon the work of Noam Nisan and Shimon Schocken. More information can be found at (www.nand2tetris.org). Where We Are

You are here We are skipping over this for now Software Human Abstract design abstract Hierarchy Thought Chapters 9, 12 H.L. Language Compiler & abstract interface Chapters 10 - 11 Operating Sys. Virtual VM Translator abstract interface Machine Chapters 7 - 8 Assembly Language

Assembler

Chapter 6 abstract interface Computer Machine Architecture abstract interface Code Chapters 4 - 5 Hardware Gate Logic abstract interface Platform Chapters 1 - 3 Electrical Chips & Engineering Physics Logic Gates

Hardware Hierarchy Some Taxonomy/History Programming languages have a rich history; a few high points:

 Machine code (hand translated binary code)

 Assembly language (low-level symbolic programming)

 Simple procedural languages, (Fortran, Basic, Algol, PL1, Pascal, )

 Simple Functional Languages (Lisp, Scheme)

 Simple object-based languages (with and w/o inheritance): (Simula 67, Smalltalk 80, Ada, C++, Objective C, In terms of Visual Basic, JavaScript) Jack functionality, Jack goes here  Modern object-oriented languages (Squeak, Java, C#) The Jack Programming Language

Jack: a simple, object-based, high-level language with a Java-like syntax Some sample applications written in Jack:

procedural programming

Pong game

Space Invaders

Tetris Hello World

/** Hello World program. */ class Main { function void main () { // Prints some text using the standard library do Output.printString("Hello World"); do Output.println(); // New line return; } }

Some observations:  Java-like syntax  Typical comments format  Standard function library  A few language-specific peculiarities Representative Programming Tasks in Jack

Jack can be used to develop most apps that come to mind, for example:

 Procedural programming: a program that computes 1 + 2 + ... + n  Object-oriented programming: a class representing bank accounts  Abstract representation: a class representing fractions  representation: a class representing linked lists  Etc.

Let’s look at these examples… Procedural Programming Example class Main { Jack program = a collection of one or more classes /** Sums up 1 + 2 + 3 + ... + n */ Jack class = a collection of function int sum (int n) { one or more var int sum, i; Note use of “~” let sum = 0; for logical negation Execution order: when we execute a let i = 1; Jack program, Main.main() starts (not “!”) while (~(i > n)) { running. let sum = sum + i; Jack subroutines: let i = i + 1; }  method return sum;  constructor }  function (static method) function void main () {  (the example on the left has var int n; functions only, as it is “object-less”) let n = Keyboard.readInt("Enter n: "); do Output.printString("The result is: "); Standard library: a of OS services do Output.printInt(sum(n)); (methods and functions) organized in 8 return; supplied classes: Math, String. Array, } Output, Keyboard, Screen, Memory, } Sys (OS API in the book). Object-Oriented Programming Example

The BankAccount class (skeletal) /** Represents a bank account. A bank account has an owner, an id, and a balance. The id values start at 0 and increment by 1 each time a new account is created. */

class BankAccount {

/** Constructs a new bank account with a 0 balance. */ constructor BankAccount new(String owner)

/** Deposits the given amount in this account. */ method void deposit(int amount)

/** Withdraws the given amount from this account. */ method void withdraw(int amount)

/** Prints the data of this account. */ method void printInfo()

/** Disposes this account. */ method void dispose()

} Object-oriented Programming Example (continued)

/** Represents a bank account. */ // Code in any other class: var int x; class BankAccount { var BankAccount b; // class variable (one per class) 2 let b = BankAccount.new("joe"); static int newAcctId; //compiler inits to 0 1 return this // Private instance variables (aka fields) The constructor returns the RAM base address of the field int id; memory block that stores the data of the newly field String owner; created BankAccount object field int balance; 2 b = BankAccount.new("joe") Calls the constructor (which creates a new /** Constructs a new bank account */ BankAccount object), then stores in variable b constructor BankAccount new (String owner) { a pointer to the object’s base let id = newAcctId; let newAcctId = newAcctId + 1; Behind the scenes (after compilation): let this.owner = owner; // b = BankAccount.new("joe") let balance = 0; push "joe" call BankAccount.new return this; 1 pop b } Explanation: the calling code pushes an argument and calls the constructor; the constructor’s code // More BankAccount methods. (not shown) creates a new object, pushes its base address onto the stack, and returns; } The calling code then pops the base address into a variable that will now point to the new object. Object-oriented Programming Example (continued) class BankAccount { ... static int nAccounts; var BankAccount b1, b2; ... field int id; let b1 = BankAccount.new("joe"); field String owner; let b2 = BankAccount.new("jane"); field int balance; do b1.deposit(5000); do b1.withdraw(1000); // Constructor ... (omitted) ... /** Handles deposits */ method void deposit (int amount) { do b1.deposit(5000) let balance = balance + amount;  In Jack, void methods are invoked return; using the keyword do (a compilation } artifact)

/** Handles withdrawals */  The object-oriented method invocation method void withdraw (int amount){ style b1.deposit(5000) has the same if (~(amount > balance)) { let balance = balance - amount; procedural semantics as } “deposit(b1,5000)” return; } Behind the scenes (after compilation): // do b1.deposit(5000) // More BankAccount methods. push b1 push 5000 } call BankAccount.deposit Object-oriented Programming Example (continued) class BankAccount { // Code in any other class: static int nAccounts; ... var int x; field int id; var BankAccount b; field String owner; field int balance; let b = BankAccount.new("joe"); // Manipulates b... // Constructor ... (omitted) do b.printInfo(); do b.dispose(); /** Prints information about this account. */ ... method void printInfo () { do Output.printInt(id); do Output.printString(owner); do b.dispose() do Output.printInt(balance); Jack has no garbage collection; return; The programmer is responsible for } explicitly recycling memory resources of objects that are no longer needed. /** Disposes of this account. */ method void dispose () { do Memory.deAlloc(this); do Memory.deAlloc(this) return; This is a call to an OS function that } knows how to recycle the memory block whose base-address is this. This // More BankAccount methods. function is part of the OS runtime library (Chapter 12). } Example

The Fraction class API (method signatures only) /** Represents a fraction data type. A fraction consists of a numerator and a denominator, both int values */

class Fraction {

/** Constructs a fraction from the given data */ constructor Fraction new(int numerator, int denominator) /** Reduces this fraction, e.g. changes 20/100 to 1/5. */ method void reduce()

/** Accessors method int getNumerator() method int getDenominator()

/** Returns the sum of this fraction and the other one */ method Fraction plus(Fraction other)

/** Returns the product of this fraction and the other one */ method Fraction product(Fraction other)

/** Prints this fraction */ method void print()

/** Disposes this fraction */ method void dispose() } Abstract Data Type Example (continued)

/** Represents a fraction data type. A fraction consists of a numerator and a denominator, both int values */ class Fraction { field int numerator, denominator; constructor Fraction new (int numerator, int denominator) { let this.numerator = numerator; let this.denominator = denominator; do reduce() // Reduces the new fraction return this } /** Reduces this fraction */ method void reduce () { // Code omitted } // A static method that computes the greatest common denominator of a and b. function int gcd (int a, int b) { // Code omitted } // Code in any other class: ... method int getNumerator () { var Fraction a, b; return numerator; let a = Fraction.new(2,5); } let b = Fraction.new(70,210); method int getDenominator () { do b.print() // prints "1/3" return denominator; ... } // (print method in next slide)

// More Fraction methods follow. Abstract Data Type Example (continued)

/** Represents a fraction data type. A fraction consists of a numerator and a denominator, both int values */ class Fraction { field int numerator, denominator;

// Constructor and previously defined methods omitted

/** Returns the sum of this fraction the other one */ method Fraction plus (Fraction other) { var int sum; let sum = (numerator * other.getDenominator()) + (other.getNumerator() * denominator()); return Fraction.new(sum , denominator * other.getDenominator()); }

// Similar fraction arithmetic methods follow, code omitted.

/** Prints this fraction */ // Code in any other class: method void print () { do Output.printInt(numerator); var Fraction a, b, c; do Output.printString("/"); let a = Fraction.new(2,3); do Output.printInt(denominator); let b = Fraction.new(1,5); return // computes c = a + b } let c = a.plus(b); do c.print(); // prints "13/15" } Data Structure Example

/** Represents a sequence of int values, implemented as a linked list. The list consists of an atom, which is an int value, and a tail, which is either a list or a null value. */ class List { field int data; field List next;

/* Creates a new list */ constructor List new (int car, List cdr) { v 5 let data = car; let next = cdr; return this; v 2 3 5 }

/* Disposes this list by recursively disposing its tail. */ method void dispose() { if (~(next = null)) { // Code in any other class: do next.dispose(); } ... do Memory.deAlloc(this); // Creates a list holding the numbers 2,3, and 5: return; var List v; } let v = List.new(5 , null); ... let v = List.new(2 , List.new(3,v)); } // class List. ... Jack Language Specification

 Syntax

 Data types

 Variable kinds

 Expressions

 Statements

calling

 Program structure

 Standard library

(for complete language specification, see the book) Jack Syntax

Note: no “==“ Note: “~” vs. “!” for not Jack Syntax (continued) Jack Data Types

Primitive types (Part of the language; Realized by the compiler):

 int 16- 2’s complement (from -32768 to 32767)

 boolean 0 and –1, representing false and true, respectively

 char unicode (or ASCII) (‘a’, ‘x’, ‘+’, ‘%’, ...)

Abstract data types (Standard language extensions; OS / standard library):

 String

 Array ... (extensible)

Application-specific types (User-defined; examples:)

 BankAccount

 Fraction

 List

 Bat / Ball . . . (as desired) Jack Variable Kinds and Jack Expressions

 A constant

 A variable name in scope (the variable may be static, field, local, or a parameter)

 The keyword this, denoting the current object

 An array element using the syntax arrayName[expression], where arrayName is a variable name of type Array in scope

 A subroutine call that returns a non-

 An expression prefixed by one of the unary operators – or ~ :

 - expression (arithmetic negation)

 ~ expression (logical negation)

 An expression of the form expression op expression where op is one of the following:

 + - * / (integer arithmetic operators)

 & | (boolean and, or operators, bit-wise for integers)

 < > = (comparison operators)

 ( expression ) (an expression within parentheses) Jack Statements let varName = expression; or let varName[expression] = expression; if (expression) { statements } else { statements } while (expression) { statements }

do function-or-method-call;

return expression; or return; Jack Subroutine Calls

General syntax: subroutineName(arg0, arg1, …) where each argument is a valid Jack expression Parameter passing is by-value (primitive types) or by-reference (object types) Example 1: Consider the function (static method): function int sqrt(int n) This function can be invoked as follows: sqrt(17) sqrt(x) sqrt((b * b) – (4 * a * c)) sqrt(a * sqrt(c - 17) + 3) etc. In all these examples the argument value is computed and passed by-value. Example 2: Consider the method: method Matrix plus (Matrix other); If u and v were variables of type Matrix, this method can be invoked using: u.plus(v) The v variable is passed by-reference, since it refers to an object. Three Styles of Jack Subroutine Call

1. subName (exp list); // method call only // functions and constructors are // always fully qualified calls 2. instanceName.SubName (exp list); // method call 3. className.subName (exp list); // function or constructor call Unusual Features of the Jack Language

In order to simplify compilation, Jack syntax has a few oddities:  The let keyword in assignments, as in let x = 0;  The do keyword in function calls, as in do reduce();  No built-in operator priority: 1 + 2 * 3 yields 9, since expressions are evaluated left-to-right; To effect the commonly expected result, use 1 + (2 * 3)  Only three primitive data types: int, boolean, char (each one of these is treated as a 16-bit value)  No casting or type-checking ; a value of any type can be assigned to a variable of any type  Array declaration: Array x; followed by x = Array.new();  Static functions are denoted by function  Constructor methods are denoted by constructor Invoking a constructor is performed using the syntax ClassName.new(argList)  Equality is tested with “=”, not “==” Any one of these language features could be modified, with a reasonable amount of work, to make it conform to a more typical syntax. Jack Program Structure

Requirements: class ClassName {  Every part in the spec can appear 0 field variable declarations; or more times static variable declarations;  The order of the field / static constructor type { parameterList ) { declarations is arbitrary local variable declarations; statements  The order of the subroutine } declarations is arbitrary method type { parameterList ) {  Each type is either int, boolean, local variable declarations; char, or a class name. statements } Jack Programs vs Files: { parameterList ) {  Each class is written in a separate file local variable declarations; (compilation unit) statements }  Jack program = collection of one or } more classes, one of which must be named Main  The Main class must contain at least one function, named main() Jack Runtime Library (what the book calls an OS) class Math { function void init() functionClass String int abs{ (int x) functionconstructor int multiply String(int new (intx, int maxLength) y) Class Array { functionmethod int void divide dispose(int x,() int y) functionmethodfunction int int min (intArray length x, new ()int(int y) size) class Output { functionmethod int char max (int charAt x, (intint y)j) methodmethod voidfunction void setCharAt disposevoid moveCursor()(int j, char(int c)i, int j) function int Classsqrt(int Screen x) { method Stringfunction appendChar void printChar(char c)(char c) } } function void clearScreen() method voidfunction eraseLastChar void printString() (String s) functionclass Memory void setColor{ (boolean b) method intfunction intValue void printInt() (int i) functionfunction functionvoid voidprintln intdrawPixel ()peek(int(int address) x, int y) method void setIntClass(int Keyboard j) { functionfunction void voidbackSpace drawLine() (int x1, int y1, function char backSpacefunction() void poke(intint x2,address, int y2) int value) } function char keyPressed() function charfunction doubleQuote voidClass ()drawRectangle Sys { (int x1, int y1, function Array alloc(int size) function char newLinefunction() char readCharint() x2, int y2) function voidfunction drawCircle void(int halt x,(): int y, int r) } function void deAlloc(Array o) function String readLine(String message) } function void error(int errorCode) } function int readInt(String message) function void wait(int duration) } } Your Task

 Write a non-trivial interactive program in Jack (e.g., a game).

 Try to use most of the Jack language features: classes, arrays, constructors, functions, methods, OS runtime calls, let, if, else, while, do, return; static, field, local and parameter variables; int, boolean and char types.

 Use the built-in Jack compiler to debug your application. Perspective

 Jack is an object-oriented language that has no inheritance (this could be introduced with a little work)

 Primitive (3 types), no runtime type checking

 Standard library of useful functions

 Q: Why should you care about Jack, and why am I making you write a program in Jack?

 A: It will make writing the compiler (Chapters 10 & 11) much easier.