Skip to content

Commit

Permalink
"Method" -> "Chunk".
Browse files Browse the repository at this point in the history
Starting to widen the semantic gap between the source language, which works
in terms of multimethods and the VM which only knows about callable chunks
of bytecode.
  • Loading branch information
munificent committed Jul 20, 2012
1 parent 8c75b8e commit c6d0298
Show file tree
Hide file tree
Showing 18 changed files with 121 additions and 59 deletions.
37 changes: 37 additions & 0 deletions doc/scratchpad/top-level-scope3.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
Any expression can appear in any order at the top level. Top-level expressions, including `def`, `var`, and `defclass` are executed top-to-bottom. Unlike in nested scopes, any name defined at the top level can be referred to anywhere in the program, even before that definition appears. However, an expression containing a name must not be *executed* before that name is defined.

Expressions appearing is variable initializers are executed immediately at the point of the definition, as are superclass specifiers. Method bodies, method patterns, field initializers, and field patterns are *not* executed immediately.

These are errors:

var first = second
var second = "value"

defclass First is Second
end
defclass Second
end

This is OK:

def method() later
var later = "ok"
print(method())

But note that this is not:

def method() later
print(method()) // later has not been defined yet
var later = "ok"

To avoid errors, all referred-to names but have defined values (i.e. the expression that defines it must have been executed) before expressions containing them are executed.

* A method body is executed when that multimethod is called and that specific
method is selected and executed.
* A method pattern is executed once when that multimethod is called after
that method's `def` expression has been executed. Field patterns are
essentially method patterns too.
* A field initializer is executed when an instance of the containing class is
constructed. (And when a value for that field is not provided?)

The general model here is that instead of trying to statically detect undefined variable access errors, we will defer much of that to runtime. This doesn't catch as many errors as we'd like, but it makes top-level forms simpler to reason about.
4 changes: 2 additions & 2 deletions src/Compiler/Compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ namespace magpie
return vm_.findNative(name);
}

gc<Method> Compiler::compileMethod(Compiler& compiler, Module* module,
gc<Chunk> Compiler::compileMethod(Compiler& compiler, Module* module,
MethodDef& method,
ErrorReporter& reporter)
{
Expand Down Expand Up @@ -121,7 +121,7 @@ namespace magpie
"Multimethods aren't implemented yet.");

gc<MethodInstance> method = multimethod->methods()[0];
gc<Method> compiled = MethodCompiler(*this, method->module()).compile(*method->def());
gc<Chunk> compiled = MethodCompiler(*this, method->module()).compile(*method->def());

vm_.methods().define(multimethod->signature(), compiled);
}
Expand Down
8 changes: 4 additions & 4 deletions src/Compiler/Compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@

namespace magpie
{
class Chunk;
class ErrorReporter;
class Method;
class Module;
class ModuleCompilation;
class Multimethod;
Expand All @@ -31,9 +31,9 @@ namespace magpie
int findNative(gc<String> name);

private:
static gc<Method> compileMethod(Compiler& compiler, Module* module,
MethodDef& method,
ErrorReporter& reporter);
static gc<Chunk> compileMethod(Compiler& compiler, Module* module,
MethodDef& method,
ErrorReporter& reporter);

Compiler(VM& vm, ErrorReporter& reporter)
: vm_(vm),
Expand Down
12 changes: 6 additions & 6 deletions src/Compiler/MethodCompiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ namespace magpie
: ExprVisitor(),
compiler_(compiler),
module_(module),
method_(new Method()),
chunk_(new Chunk()),
code_(),
numLocals_(0),
numTemps_(0),
maxSlots_(0)
{}

gc<Method> MethodCompiler::compile(MethodDef& method)
gc<Chunk> MethodCompiler::compile(MethodDef& method)
{
Resolver::resolve(compiler_, *module_, method);

Expand All @@ -43,9 +43,9 @@ namespace magpie
compile(method.body(), numParamSlots);
write(OP_RETURN, numParamSlots);

method_->setCode(code_, maxSlots_);
chunk_->setCode(code_, maxSlots_);

return method_;
return chunk_;
}

void MethodCompiler::compileParam(gc<Pattern> param, int& slot)
Expand Down Expand Up @@ -482,12 +482,12 @@ namespace magpie

int MethodCompiler::compileConstant(const NumberExpr& expr)
{
return method_->addConstant(new NumberObject(expr.value()));
return chunk_->addConstant(new NumberObject(expr.value()));
}

int MethodCompiler::compileConstant(const StringExpr& expr)
{
return method_->addConstant(new StringObject(expr.value()));
return chunk_->addConstant(new StringObject(expr.value()));
}

void MethodCompiler::write(OpCode op, int a, int b, int c)
Expand Down
6 changes: 3 additions & 3 deletions src/Compiler/MethodCompiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ namespace magpie
private:
MethodCompiler(Compiler& compiler, Module* module);

gc<Method> compile(MethodDef& method);
gc<Chunk> compile(MethodDef& method);

void compileParam(gc<Pattern> param, int& slot);
void compileParamField(gc<Pattern> param, int slot);
Expand Down Expand Up @@ -90,8 +90,8 @@ namespace magpie
// The module containing the method being compiled.
Module* module_;

// The method being compiled.
gc<Method> method_;
// The chunk being compiled.
gc<Chunk> chunk_;

Array<instruction> code_;

Expand Down
34 changes: 17 additions & 17 deletions src/VM/Fiber.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ namespace magpie
nearestCatch_()
{}

void Fiber::init(gc<Method> method)
void Fiber::init(gc<Chunk> chunk)
{
ASSERT(stack_.count() == 0, "Cannot re-initialize Fiber.");
ASSERT(callFrames_.count() == 0, "Cannot re-initialize Fiber.");

call(method, 0);
call(chunk, 0);
}

FiberResult Fiber::run()
Expand All @@ -29,7 +29,7 @@ namespace magpie
if (Memory::checkCollect()) return FIBER_DID_GC;

CallFrame& frame = callFrames_[-1];
instruction ins = frame.method->code()[frame.ip++];
instruction ins = frame.chunk->code()[frame.ip++];
OpCode op = GET_OP(ins);

switch (op)
Expand All @@ -46,7 +46,7 @@ namespace magpie
{
int index = GET_A(ins);
int slot = GET_B(ins);
store(frame, slot, frame.method->getConstant(index));
store(frame, slot, frame.chunk->getConstant(index));
break;
}

Expand Down Expand Up @@ -104,7 +104,7 @@ namespace magpie

// The next instruction is a pseudo-instruction containing the offset
// to jump to.
instruction jump = frame.method->code()[frame.ip++];
instruction jump = frame.chunk->code()[frame.ip++];
ASSERT(GET_OP(jump) == OP_JUMP,
"Pseudo-instruction after OP_TEST_FIELD must be OP_JUMP.");

Expand Down Expand Up @@ -284,7 +284,7 @@ namespace magpie

case OP_CALL:
{
gc<Method> method = vm_.methods().get(GET_A(ins));
gc<Chunk> method = vm_.methods().get(GET_A(ins));
int firstArg = GET_B(ins);
int stackStart = frame.stackStart + firstArg;
call(method, stackStart);
Expand All @@ -305,7 +305,7 @@ namespace magpie
gc<Object> result = loadSlotOrConstant(frame, GET_A(ins));
callFrames_.removeAt(-1);

// Discard any try blocks enclosed in the current method.
// Discard any try blocks enclosed in the current chunk.
while (!nearestCatch_.isNull() &&
(nearestCatch_->callFrame() >= callFrames_.count()))
{
Expand All @@ -314,17 +314,17 @@ namespace magpie

if (callFrames_.count() > 0)
{
// Give the result back and resume the calling method.
// Give the result back and resume the calling chunk.
CallFrame& caller = callFrames_[-1];
instruction callInstruction = caller.method->code()[caller.ip - 1];
instruction callInstruction = caller.chunk->code()[caller.ip - 1];
ASSERT(GET_OP(callInstruction) == OP_CALL,
"Should be returning to a call.");

store(caller, GET_C(callInstruction), result);
}
else
{
// The last method has returned, so end the fiber.
// The last chunk has returned, so end the fiber.
// TODO(bob): Do we care about the result value?
return FIBER_DONE;
}
Expand Down Expand Up @@ -379,7 +379,7 @@ namespace magpie
{
// Walk the stack.
CallFrame& frame = callFrames_[-1];
int numSlots = frame.stackStart + frame.method->numSlots();
int numSlots = frame.stackStart + frame.chunk->numSlots();

// Only reach slots that are still in use. We don't shrink the stack, so it
// may have dead slots at the end that are safe to collect.
Expand All @@ -405,20 +405,20 @@ namespace magpie

for (int i = 0; i < callFrames_.count(); i++)
{
Memory::reach(callFrames_[i].method);
Memory::reach(callFrames_[i].chunk);
}
}

void Fiber::call(gc<Method> method, int stackStart)
void Fiber::call(gc<Chunk> chunk, int stackStart)
{
// Allocate slots for the method.
// TODO(bob): Make this a single operation on Array.
while (stack_.count() < stackStart + method->numSlots())
while (stack_.count() < stackStart + chunk->numSlots())
{
stack_.add(gc<Object>());
}

callFrames_.add(CallFrame(method, stackStart));
callFrames_.add(CallFrame(chunk, stackStart));
}

bool Fiber::throwError(gc<Object> error)
Expand All @@ -435,7 +435,7 @@ namespace magpie
frame.ip = nearestCatch_->offset();

// The next instruction is a pseudo-op identifying where the error is.
instruction errorIns = frame.method->code()[frame.ip];
instruction errorIns = frame.chunk->code()[frame.ip];
ASSERT(GET_OP(errorIns) == OP_MOVE,
"Expect pseudo-instruction at beginning of catch code.");
int errorSlot = GET_A(errorIns);
Expand All @@ -452,7 +452,7 @@ namespace magpie
{
if (IS_CONSTANT(index))
{
return frame.method->getConstant(GET_CONSTANT(index));
return frame.chunk->getConstant(GET_CONSTANT(index));
}
else
{
Expand Down
18 changes: 9 additions & 9 deletions src/VM/Fiber.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace magpie
// The reason Fiber::run() returned.
enum FiberResult
{
// The fiber's entry method has completed and the fiber is complete.
// The fiber's entry chunk has completed and the fiber is complete.
FIBER_DONE = 0,

// A garbage collection is happened, so the fiber has moved in memory.
Expand All @@ -30,7 +30,7 @@ namespace magpie
public:
Fiber(VM& vm);

void init(gc<Method> method);
void init(gc<Chunk> chunk);
FiberResult run();

virtual void reach();
Expand All @@ -40,23 +40,23 @@ namespace magpie
{
// So that we can use CallFrames in an Array<T> by value.
CallFrame()
: method(),
: chunk(),
ip(0),
stackStart(0)
{}

CallFrame(gc<Method> method, int stackStart)
: method(method),
CallFrame(gc<Chunk> chunk, int stackStart)
: chunk(chunk),
ip(0),
stackStart(stackStart)
{}

gc<Method> method;
gc<Chunk> chunk;
int ip;
int stackStart;
};

void call(gc<Method> method, int stackStart);
void call(gc<Chunk> chunk, int stackStart);

// Loads a slot for the given callframe.
inline gc<Object> load(const CallFrame& frame, int slot)
Expand Down Expand Up @@ -108,10 +108,10 @@ namespace magpie
// is unhandled and the fiber will abort.
gc<CatchFrame> parent_;

// Index of the CallFrame for the method containing this catch.
// Index of the CallFrame for the chunk containing this catch.
int callFrame_;

// The offset of the instruction to jump to in the containing method to
// The offset of the instruction to jump to in the containing chunk to
// start executing the catch handler.
int offset_;
};
Expand Down
14 changes: 7 additions & 7 deletions src/VM/Method.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,28 @@

namespace magpie
{
void Method::setCode(const Array<instruction>& code, int numSlots)
void Chunk::setCode(const Array<instruction>& code, int numSlots)
{
// TODO(bob): Copying here is lame!
code_ = code;
numSlots_ = numSlots;
}

int Method::addConstant(gc<Object> constant)
int Chunk::addConstant(gc<Object> constant)
{
// TODO(bob): Should check for duplicates. Only need one copy of any
// given constant.
constants_.add(constant);
return constants_.count() - 1;
}

gc<Object> Method::getConstant(int index) const
gc<Object> Chunk::getConstant(int index) const
{
ASSERT_INDEX(index, constants_.count());
return constants_[index];
}

void Method::debugTrace() const
void Chunk::debugTrace() const
{
using namespace std;

Expand All @@ -37,7 +37,7 @@ namespace magpie
}
}

void Method::debugTrace(instruction ins) const
void Chunk::debugTrace(instruction ins) const
{
using namespace std;

Expand Down Expand Up @@ -135,12 +135,12 @@ namespace magpie
cout << endl;
}

void Method::reach()
void Chunk::reach()
{
Memory::reach(constants_);
}

void MethodScope::define(gc<String> name, gc<Method> method)
void MethodScope::define(gc<String> name, gc<Chunk> method)
{
names_.add(name);
methods_.add(method);
Expand Down
Loading

0 comments on commit c6d0298

Please sign in to comment.