Some design notes.

This commit is contained in:
Bob Nystrom
2013-11-13 17:09:55 -08:00
parent 85d9bffa08
commit 5ac4087d0c
2 changed files with 131 additions and 0 deletions

55
doc/beta-style inner.txt Normal file
View File

@ -0,0 +1,55 @@
class Base {
foo {
io.write("a")
inner
io.write("b")
}
}
class Mid is Base {}
class Derived is Mid {
foo {
io.write("c")
}
}
var d = Derived.new
d.foo
// find base-most method "foo" (on Base)
// invoke it
// when inner is hit, walk down inheritance chain to find nearest override
// (in Derived)
// invoke it
for every class, store two lists:
1. list of methods this class defines.
2. mapping of method name to which method body should be invoked first for
that method. this will be the base-most class's definition of a given
method name.
typedef struct
{
// The method body to invoke. This will be the base-most definition of a
// method for a given name.
Method* method;
// If [method] calls `inner`, this is the class whose inner method is invoked.
// If *that* method in turn calls `inner`, we look up the [inner] property of
// this method on that class's dispatch table to chain to the next one.
//
// This is `NULL` if there is no inner method to call.
ClassObj* inner;
} Dispatch;
typedef struct
{
Dispatch dispatchTable[MAX_SYMBOLS];
} ClassObj;
with normal overridding, can unify those. with inner(), need both.
whenever a method is invoked, look it up in 2, then call that method body.

View File

@ -0,0 +1,76 @@
Q1: What does this resolve to:
foo(arg)
It could be:
1. this.foo(bar)
2. EnclosingClass.foo(bar) // i.e. a static method call
3. a call to a top-level function foo()
If we adopt the idea that a module is just a class definition (with some
syntactic differences) and classes can be nested, then 3 really means "a call
to a static method on the class surrounding the enclosing class".
I *don't* think we want the answer to the question to vary based on the name
in question. We can't rely on name resolution to disambiguate because we don't
know the full set of surrounding names in a single pass compiler. Also, it's
semantically squishier.
I think the right answer is 1, it's an implicit call on this. That's what you
want most often, I think. For imported modules, we could import them with a
"prefix" (really import them as objects bound to named variables), so calling
a top-level function in another module would be something like:
someModule.foo(arg)
This leaves the question of how *do* you call top level functions in your own
module? I.e., how do we call foo here:
def foo(arg) { io.write("called foo!") }
class SomeClass {
bar {
// Want to call foo here...
}
}
This is analogous to:
class SomeModule {
static foo(arg) { io.write("called foo!") }
class SomeClass {
bar {
// Want to call foo here...
}
}
}
The obvious solution is to use the class name:
class SomeModule {
static foo(arg) { io.write("called foo!") }
class SomeClass {
bar {
SomeModule.foo(arg)
}
}
}
Which just leaves the question of what the class name of a top-level "module
class" is.
Idea: it's unnamed, so you just use a leading ".":
def foo(arg) { io.write("called foo!") }
class SomeClass {
bar {
.foo(arg)
}
}
This mirrors C++'s unnamed scope thing:
::foo(arg);