1
0
forked from Mirror/wren

Unify code for ending compiler.

There's now a single code path for the end of a chunk of bytecode, so
we can eventually put code there for capturing closures.
This commit is contained in:
Bob Nystrom
2013-12-01 14:59:56 -08:00
parent 19811143a0
commit e4043d69b4
3 changed files with 78 additions and 27 deletions

View File

@ -211,6 +211,12 @@ static int initCompiler(Compiler* compiler, Parser* parser,
compiler->locals[0].depth = -1;
// The initial scope for function or method is a local scope.
// TODO(bob): Need to explicitly pop this scope at end of fn/method so
// that we can correctly close over locals declared at top level of member.
// also, when done compiling fn/method, need to count total number of
// upvalues and store in fnobj. note: have to make sure we include upvalues
// added because a fn within this one closed over something outside of this
// one and we had to add upvalue here to flatten the closure.
compiler->scopeDepth = 0;
}
@ -227,6 +233,31 @@ static int initCompiler(Compiler* compiler, Parser* parser,
return addConstant(parent, OBJ_VAL(compiler->fn));
}
// Emits one bytecode instruction or argument.
static int emit(Compiler* compiler, Code code)
{
compiler->fn->bytecode[compiler->numCodes++] = code;
return compiler->numCodes - 1;
}
// Finishes [compiler], which is compiling a function, method, or chunk of top
// level code. If there is a parent compiler, then this emits code in the
// parent compiler to load the resulting function.
static void endCompiler(Compiler* compiler, int constant)
{
// End the function's code.
emit(compiler, CODE_END);
// TODO(bob): will need to compile different code to capture upvalues if fn
// has them.
if (compiler->parent != NULL)
{
// In the function that contains this one, load the resulting function object.
emit(compiler->parent, CODE_CONSTANT);
emit(compiler->parent, constant);
}
}
// Outputs a compile or syntax error.
static void error(Compiler* compiler, const char* format, ...)
{
@ -649,14 +680,7 @@ static void consume(Compiler* compiler, TokenType expected,
}
}
// Code generation utilities ---------------------------------------------------
// Emits one bytecode instruction or argument.
static int emit(Compiler* compiler, Code code)
{
compiler->fn->bytecode[compiler->numCodes++] = code;
return compiler->numCodes - 1;
}
// Variables and scopes --------------------------------------------------------
// Parses a name token and declares a variable in the current scope with that
// name. Returns its symbol.
@ -770,6 +794,9 @@ static void popScope(Compiler* compiler)
emit(compiler, CODE_POP);
}
// TODO(bob): Need to emit code to capture upvalue for any local going out of
// scope now that is closed over.
compiler->scopeDepth--;
}
@ -796,6 +823,15 @@ static int resolveName(Compiler* compiler, int* isGlobal)
}
}
// TODO(bob): Closures!
// look in current upvalues to see if we've already closed over it
// if so, just use that
// walk up parent chain looking in their local scopes for variable
// if we find it, need to close over it here
// add upvalue to fn being compiled
// return index of upvalue
// instead of isGlobal, should be some local/upvalue/global enum
// If we got here, it wasn't in a local scope, so try the global scope.
*isGlobal = 1;
return findSymbol(&compiler->parser->vm->globalSymbols, start, length);
@ -986,11 +1022,7 @@ static void function(Compiler* compiler, int allowAssignment)
expression(&fnCompiler, 0);
}
emit(&fnCompiler, CODE_END);
// Compile the code to load it.
emit(compiler, CODE_CONSTANT);
emit(compiler, constant);
endCompiler(&fnCompiler, constant);
}
static void field(Compiler* compiler, int allowAssignment)
@ -1047,6 +1079,8 @@ static void name(Compiler* compiler, int allowAssignment)
// Compile the right-hand side.
statement(compiler);
// TODO(bob): Handle assigning to upvalue.
if (isGlobal)
{
emit(compiler, CODE_STORE_GLOBAL);
@ -1061,6 +1095,8 @@ static void name(Compiler* compiler, int allowAssignment)
return;
}
// TODO(bob): Handle reading upvalue.
// Otherwise, it's just a variable access.
if (isGlobal)
{
@ -1514,12 +1550,11 @@ void method(Compiler* compiler, Code instruction, SignatureFn signature)
emit(&methodCompiler, 0);
}
emit(&methodCompiler, CODE_END);
endCompiler(&methodCompiler, constant);
// Compile the code to define the method it.
// Compile the code to define the method.
emit(compiler, instruction);
emit(compiler, symbol);
emit(compiler, constant);
}
// Compiles a name-binding statement.
@ -1670,7 +1705,7 @@ ObjFn* wrenCompile(WrenVM* vm, const char* source)
emit(&compiler, CODE_POP);
}
emit(&compiler, CODE_END);
endCompiler(&compiler, -1);
unpinObj(vm);

View File

@ -554,7 +554,7 @@ Value interpret(WrenVM* vm, ObjFn* fn)
{
int type = instruction;
int symbol = READ_ARG();
int constant = READ_ARG();
Value method = POP();
ObjClass* classObj = AS_CLASS(PEEK());
switch (type)
@ -576,8 +576,7 @@ Value interpret(WrenVM* vm, ObjFn* fn)
break;
}
ObjFn* body = AS_FN(frame->fn->constants[constant]);
classObj->methods[symbol].fn = body;
classObj->methods[symbol].fn = AS_FN(method);
DISPATCH();
}

View File

@ -28,17 +28,21 @@ typedef enum
// Pop a superclass off the stack, then push a new class that extends it.
CODE_SUBCLASS,
// Add a method for symbol [arg1] with body stored in constant [arg2] to the
// class on the top of stack. Does not modify the stack.
// Define a method for symbol [arg] whose body is the function popped off the
// top of the stack. The class receiving the method is on top of the stack
// (after the function is popped off) and remains after this.
CODE_METHOD_INSTANCE,
// Add a method for symbol [arg1] with body stored in constant [arg2] to the
// metaclass of the class on the top of stack. Does not modify the stack.
// Define a method for symbol [arg] whose body is the function popped off the
// top of the stack. The class receiving the method is the metaclass of the
// class on top of the stack (after the function is popped off) and remains
// after this.
CODE_METHOD_STATIC,
// Add a constructor method for symbol [arg1] with body stored in constant
// [arg2] to the metaclass of the class on the top of stack. Does not modify
// the stack.
// Define a constructor method for symbol [arg] whose body is the function
// popped off the top of the stack. The class receiving the method is the
// metaclass of the class on top of the stack (after the function is popped
// off) and remains after this.
CODE_METHOD_CTOR,
// Create a new list with [arg] elements. The top [arg] values on the stack
@ -115,6 +119,19 @@ typedef enum
CODE_END
} Code;
// TODO(bob): add opcode for closure
// functions with no upvalues can just be loaded as constants like normal
// functions which do have upvalues need a CODE_CLOSURE op. it takes the
// constant index of the fn as an argument.
// it loads that fn, then wraps it in a closure object.
// then it captures all of the upvalues.
// those can either be upvalues for locals, or upvalues that close over an
// outer upvalue.
//
// then add ops for loading and storing an upvalue. take upvalue index as arg.
typedef struct
{
// TODO(bob): Make this dynamically sized.