// defined my grammar p ::= s1 … sn s ::= class C with x1, …, xn; def C.m(x1, …, xn) { s1 … sm } var x = e; this.x = e; e; e ::= x this this.x new C(e1, …, en) e.m(e1, …, en) // example program class Z; def Z.init() {} def Z.add(n) { return n; } def Z.multiply(n) { return this; } class S with n; def S.init(num) { this.n = num; } def S.add(other) { return new S(this.n.add(other)); } def S.multiply(other) { var multMinusOne = this.n.multiply(other); return other.add(multMinusOne); } var two = new S(new S(new Z())); var three = new S(two); two.multiply(three); // think about strategy - one kind of object - all objects have all fields and methods - wastes a lot of space - dispatch is fast - does not require types - must define struct Object first, because it is used all over - which means we must collect field and method names in a set, then produce this at the end - must declare constructors before methods, because they might be called - method definition order doesn't matter - must define constructors after methods, because we take the address of the functions - class must know its methods, so we have to collect those // defined output - see prototype.c // commented output to describe different parts // define how we will produce the output - constant bits - prelude - main header - main footer - separate text strings - constructor decls - methods - main body - data structures - all methods (have to do a set union, so can't just append) - all fields (have to do a set union, so can't just append) - set of ClassDecl objects (for producing constructors, at end) - and ClassDecl will build up a set of method names // implement output // test it! - one construct at a time. E.g. an early test case was: class Z; - then a later test case was class Z; def Z.init() {} def Z.self() { new Z(); } new Z()