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:
@ -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);
|
||||
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
Reference in New Issue
Block a user