The goal of the next milestone is to use your pretty new 164 language to implement layout of BAH (which is essentially some subset of HTML). You will take your BAH grammar from project two and have it generate an AST (which we call the DOM). You will pass this AST into a 164 program, where it will be represented with inheritance and 164 lists. You will use your nice iterators, for loops, coroutines, and so forth to traverse this DOM and call Python functions we provide to draw on the screen.

Inheritance

There are two common types of object-oriented programming. One is class-based programming, as in C++ or Java, with which most of us are familiar. It separates classes from objects: classes define behavior and you instantiate them with objects, which contain their own state. The other type, which is less widely-used today but is in fact used in JavaScript, uses prototypes. Here, there is no distinction between classes and objects. Everything is an object, and you clone existing objects. Note that this also more easily allows more dynamic behavior, e.g. adding new fields or methods to an object or many objects at runtime. As usual, Wikipedia has more information on prototypes.

For this milestone, it will likely be easier to implement prototype-based inheritance, since we already have objects and fields.

Using inheritance, how will we create an object? We might do something like this.

def Account = {}
    
Account.new = lambda() {
    o = {}
    o.balance = 0
    o.deposit = lambda (self,v) {
        self.balance = self.balance + v
    }
    o
}

def a = Account.new()
a.deposit(a,10)
Notice how Account here is an object like any other. We give it a method new that creates a new Account object. This is a good start, but it doesn't do any inheritance. It's also not very object-oriented: each Account object will have its own copy of all of its methods, which will waste space.

We can adjust the above code to be a bit cleaner.

def Account = {}

Account.balance = 0
    
Account.new = lambda() {
    o = {}
    # Somehow set o's parent to Account
    o
}
    
Account.deposit = lambda (self,v) {
    self.balance = self.balance + v
}

def a = Account.new()
a.deposit(a,10)
This stops allows us to only keep one copy of each method, which is nice. But how do we want to handle inheritance? How can we link an object to its parent? There are really two questions here: how the source will indicate the linking, and how the interpreter will implement it. In section, we wrote code like the following for the constructor:
Account.new = lambda() {
    o = {}
    o.__parent__ = Account
    o
}
(Actually, the code we wrote was a bit sillier, but that's neither here nor there.) In lecture a few weeks ago, Ras wrote something like the following.
Account.new = lambda(self) {
    o = {}
    setmetatable(o, self)
    self.__index = self
    o
}
def a = Account.new(Account)
The first method, using a single parent pointer, is likely slightly simpler. The second, using a metatable like Lua (see lecture notes or the Lua book for details), is slightly more complicated, but more powerful. It allows more complicated objects, such as those that support multiple inheritance or private methods (see here and here for some examples of why this metatable/index strategy is more powerful). It also allows the user to implement whatever type of inheritance they want, potentially including different types for different parts of a program.

Finally, there is the question of how the interpreter handles inheritance. Previously, given code like x.foo, your interpreter likely essentially did x["foo"] and returned its value. With inheritance, we have to handle the case where an object does not itself contain a key. How can we use the mechanisms shown above to handle a missing key? The key lies in the meaning of inheritance.

Interoperability with Python

In this milestone, you will have to ensure that your code is interoperable with Python. Specifically, you will have to write a 164 program that takes as input a Python data structure (the BAH DOM) and that calls into Python functions (to draw things on the screen). The third milestone will require even more interoperability since it adds support for scripting.

The first question is how we convert between Python and 164 data structures. This answer will of course depend on how you implement things, but we can discuss some general strategies. Luckily, since the 164 interpreter is implemented in Python, we are already the problem is made easier for us. 164 ints and strings are represented with Python ints and strings, so no conversion is necessary between them. Lists, however, may well be harder.

Many people seemed to implement 164 lists with 164 objects, which themselves use Python dicts. For example, the Python list [1,2,3] might be represented as {0:1, 1:2, 2:3} (there are other representations, but I believe this is common to many of them). So to pass this Python list into 164 code, we have to convert from one representation to the other. Luckily, this is not a difficult problem: a simple loop will do the trick.

We can use some Python magic to simplify the code a little, as the following interpreter example shows.

>>> dict(enumerate(["a","b","c"]))
{0: 'a', 1: 'b', 2: 'c'}
Why write out a list when you can use magic Python built-ins? Similarly, if you decide to implement your DOM with Python classes (which you don't have to do, but I'm mentioning this just in case you do), the following example (which also shows how to use Python inheritance) will be helpful.
>>> class A:
...     def __init__(self, x):
...         self.x = x
...     def getX(self):
...         return self.x
... 
>>> class B(A):
...     def __init__(self, x):
...         self.x = x + 1
... 
>>> a = A(42)
>>> a.__dict__
{'x': 42}
With these tricks, you should be able to convert between Python and 164 data structures without much trouble.

The other problem requires calling Python code from within 164. How can we add support for this? For example, we will give you a Python method something like def drawText(text, x, y), which you will have to call in 164. We would like to call it directly, e.g. with drawText(t, 0, 0), but this will try to call a normal 164 function.

To get around this, we have to modify our grammar. Specifically, we need some way of making external or native calls. We also have to consider how to handle Python's modules. How do we know in which file drawText resides? We might want to add support to 164 for something like the following.

native canvas.drawText(t, x, y)
This 164 code uses a keyword to denote that it calls Python and the dot to distinguish between the module and the function name. Note that this syntax exactly may not quite suffice for this project (take a close look at the functions will need to call), but it's the right track. You can also, of course, choose a different syntax or style.

Finally, we have to decide how to implement these native calls. We can generate bytecode for them, but how do we modify our interpreter to actually make the call? The following Python code gives one way to do this.

# Say the code we want to call is in foo.py.
# So this might correspond to
#  native foo.bar(42)
module = __import__("foo")
func = getattr(module, "bar")
rv = func(42)
print rv
This code allows you to import a module given as a string and call a function in that module that is also given as a string.