Try Firefox!

SI413 Lab 12: Dynamic type checking

This lab is a continuation of Lab 8, Lab 9, Lab 10, and Lab 11. Our SPL interpreter up to this point is completely untyped, meaning that the interpreter itself has no clue what types the objects it manipulates have. Thus. it'll happily do things like this:
write 5 < 8;
1
write 4/true;
4
if (5) { write 555; } else { write 444; }
555
new f := 8;
write f(3);
Segmentation fault
That last one, in particular, is a bad thing. We need to add type checking and type-aware behavior to our interpreter! We'll add a dynamic type system so that instead of the above garbage, we get this kind of thing:
write 5 < 8;
true
if (5) { write 555; } else { write 444; }
Non-bool value in a bool context!
new f := 8;
write f(3);
Error!  Applying non-function object!
undef
      
Dynamic type checking
Recall that dynamic type checking means checking object types at run time. Also recall that the basic way we do this is to have each object represented inside the interpreter by a structure that contains a type field as well as the actual value. I'm going to go ahead and give you a definition for this, which you can use or not as you see fit:
class Closure; // forward declaration
// Structure for representing SPL objects by a type/value pair
class Obj
{
public:
  int otype; union { int ival; bool bval; Closure *fval; };
};

// Object "otype" codes: "_oundef" means undefined - it comes in handy
const int _oundef = 0, _oint = 1, _obool = 2, _ofun = 3;

// Handy functions for creating an Obj from a value
Obj mkobj(int k)      { Obj o; o.otype = _oint;  o.ival = k; return o; }
Obj mkobj(bool b)     { Obj o; o.otype = _obool; o.bval = b; return o; }
Obj mkobj(Closure* p) { Obj o; o.otype = _ofun;  o.fval = p; return o; }
Hopefully you remember our discussion of union types in C/C++ ... if not, flag me down in lab. You need to add the above definitions to your code, change frames to be name-Obj maps instead of name-int maps, and change eval to return an Obj instead of an int. Make the above changes, comment out the entire body of eval, and add to the top of eval a line to create an _oundef Obj, and then a line to return that Obj. This version of your program ought to actually compile!

Step 1
The key to making a change like this managable is to be able to do it in small, testable chunks. So, get the eval cases _num, _bool, _lambda, and _write to work, and leave the rest commented out. You ought to be able to do something like this:
write 5;
5
write true;
true
write lambda x { res := 6; };
function:0x8072c78
Notice how write reacts correctly to the type of the object you send it. Of course you won't be able to call functions or do arithmetic or any of that.
Note 1: you might need to add the _bool case if your code doesn't have it already. Similar to _num.
Note 2: right now your "statements" return 0 from eval(). They should instead return an Obj with type _oundef.

Step 2
Next get eval cases _new, _asn, and _id to work. You ought to be able to do something like this:
new x := false;
write x;
false
x := 0;
write x;
0
x := lambda x { res := 6; };
write x;
function:0x8072cd8

Step 3
Next get eval cases _binop and _unop working. Remember: +,-,*,/,<, >, <=, >= should only work on integer objects, and, or and ! should only work on boolean objects. Finally, = and != should work on any type of object, but the two operands had better be of the same type!. If you get this right, you should be able to do something like this:
new x := 6;
write x > 2 and x < 10;
true
write x*5 + (x != 0);
Type mismatch in +!
undef
write (x = 6) != false;
true
write x = lambda x { res := x*x; };
Type mismatch in =!
new f := lambda x { res := x*x; };
write f = f;
true
write !false;
true;
write 5 = true;
type mismatch for eq!

Step 4
Finally, add everything else, i.e. cases _if, _while, and _funcall. Now you should be able to do fun things like this:
new f := lambda x { 3+4; }; # Since res never gets set, this returns an Obj of type _oundef
new err := f(0);
write err;
undef
new g := lambda x # g(x) returns the floor of sqrt(x)
         {
           if (x >= 0)
           {
             res := 0;
             while((res+1)*(res+1) <= x) { res := res + 1; }
           }
         };
write g(9);
3
write g(67);
8
write g(-4);
undef

Submission
Make sure your code sits in a directory called lab12. Make sure your code contains your names. Submit via the usual submit script before the deadline.


Christopher W Brown
Last modified: Thu Nov 12 11:41:02 EST 2009