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:
parent
19811143a0
commit
e4043d69b4
@ -211,6 +211,12 @@ static int initCompiler(Compiler* compiler, Parser* parser,
|
|||||||
compiler->locals[0].depth = -1;
|
compiler->locals[0].depth = -1;
|
||||||
|
|
||||||
// The initial scope for function or method is a local scope.
|
// 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;
|
compiler->scopeDepth = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -227,6 +233,31 @@ static int initCompiler(Compiler* compiler, Parser* parser,
|
|||||||
return addConstant(parent, OBJ_VAL(compiler->fn));
|
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.
|
// Outputs a compile or syntax error.
|
||||||
static void error(Compiler* compiler, const char* format, ...)
|
static void error(Compiler* compiler, const char* format, ...)
|
||||||
{
|
{
|
||||||
@ -649,14 +680,7 @@ static void consume(Compiler* compiler, TokenType expected,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Code generation utilities ---------------------------------------------------
|
// Variables and scopes --------------------------------------------------------
|
||||||
|
|
||||||
// Emits one bytecode instruction or argument.
|
|
||||||
static int emit(Compiler* compiler, Code code)
|
|
||||||
{
|
|
||||||
compiler->fn->bytecode[compiler->numCodes++] = code;
|
|
||||||
return compiler->numCodes - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parses a name token and declares a variable in the current scope with that
|
// Parses a name token and declares a variable in the current scope with that
|
||||||
// name. Returns its symbol.
|
// name. Returns its symbol.
|
||||||
@ -770,6 +794,9 @@ static void popScope(Compiler* compiler)
|
|||||||
emit(compiler, CODE_POP);
|
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--;
|
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.
|
// If we got here, it wasn't in a local scope, so try the global scope.
|
||||||
*isGlobal = 1;
|
*isGlobal = 1;
|
||||||
return findSymbol(&compiler->parser->vm->globalSymbols, start, length);
|
return findSymbol(&compiler->parser->vm->globalSymbols, start, length);
|
||||||
@ -986,11 +1022,7 @@ static void function(Compiler* compiler, int allowAssignment)
|
|||||||
expression(&fnCompiler, 0);
|
expression(&fnCompiler, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
emit(&fnCompiler, CODE_END);
|
endCompiler(&fnCompiler, constant);
|
||||||
|
|
||||||
// Compile the code to load it.
|
|
||||||
emit(compiler, CODE_CONSTANT);
|
|
||||||
emit(compiler, constant);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void field(Compiler* compiler, int allowAssignment)
|
static void field(Compiler* compiler, int allowAssignment)
|
||||||
@ -1047,6 +1079,8 @@ static void name(Compiler* compiler, int allowAssignment)
|
|||||||
// Compile the right-hand side.
|
// Compile the right-hand side.
|
||||||
statement(compiler);
|
statement(compiler);
|
||||||
|
|
||||||
|
// TODO(bob): Handle assigning to upvalue.
|
||||||
|
|
||||||
if (isGlobal)
|
if (isGlobal)
|
||||||
{
|
{
|
||||||
emit(compiler, CODE_STORE_GLOBAL);
|
emit(compiler, CODE_STORE_GLOBAL);
|
||||||
@ -1061,6 +1095,8 @@ static void name(Compiler* compiler, int allowAssignment)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(bob): Handle reading upvalue.
|
||||||
|
|
||||||
// Otherwise, it's just a variable access.
|
// Otherwise, it's just a variable access.
|
||||||
if (isGlobal)
|
if (isGlobal)
|
||||||
{
|
{
|
||||||
@ -1514,12 +1550,11 @@ void method(Compiler* compiler, Code instruction, SignatureFn signature)
|
|||||||
emit(&methodCompiler, 0);
|
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, instruction);
|
||||||
emit(compiler, symbol);
|
emit(compiler, symbol);
|
||||||
emit(compiler, constant);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compiles a name-binding statement.
|
// Compiles a name-binding statement.
|
||||||
@ -1670,7 +1705,7 @@ ObjFn* wrenCompile(WrenVM* vm, const char* source)
|
|||||||
emit(&compiler, CODE_POP);
|
emit(&compiler, CODE_POP);
|
||||||
}
|
}
|
||||||
|
|
||||||
emit(&compiler, CODE_END);
|
endCompiler(&compiler, -1);
|
||||||
|
|
||||||
unpinObj(vm);
|
unpinObj(vm);
|
||||||
|
|
||||||
|
|||||||
@ -554,7 +554,7 @@ Value interpret(WrenVM* vm, ObjFn* fn)
|
|||||||
{
|
{
|
||||||
int type = instruction;
|
int type = instruction;
|
||||||
int symbol = READ_ARG();
|
int symbol = READ_ARG();
|
||||||
int constant = READ_ARG();
|
Value method = POP();
|
||||||
ObjClass* classObj = AS_CLASS(PEEK());
|
ObjClass* classObj = AS_CLASS(PEEK());
|
||||||
|
|
||||||
switch (type)
|
switch (type)
|
||||||
@ -576,8 +576,7 @@ Value interpret(WrenVM* vm, ObjFn* fn)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjFn* body = AS_FN(frame->fn->constants[constant]);
|
classObj->methods[symbol].fn = AS_FN(method);
|
||||||
classObj->methods[symbol].fn = body;
|
|
||||||
DISPATCH();
|
DISPATCH();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -28,17 +28,21 @@ typedef enum
|
|||||||
// Pop a superclass off the stack, then push a new class that extends it.
|
// Pop a superclass off the stack, then push a new class that extends it.
|
||||||
CODE_SUBCLASS,
|
CODE_SUBCLASS,
|
||||||
|
|
||||||
// Add a method for symbol [arg1] with body stored in constant [arg2] to the
|
// Define a method for symbol [arg] whose body is the function popped off the
|
||||||
// class on the top of stack. Does not modify the stack.
|
// 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,
|
CODE_METHOD_INSTANCE,
|
||||||
|
|
||||||
// Add a method for symbol [arg1] with body stored in constant [arg2] to the
|
// Define a method for symbol [arg] whose body is the function popped off the
|
||||||
// metaclass of the class on the top of stack. Does not modify the stack.
|
// 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,
|
CODE_METHOD_STATIC,
|
||||||
|
|
||||||
// Add a constructor method for symbol [arg1] with body stored in constant
|
// Define a constructor method for symbol [arg] whose body is the function
|
||||||
// [arg2] to the metaclass of the class on the top of stack. Does not modify
|
// popped off the top of the stack. The class receiving the method is the
|
||||||
// the stack.
|
// metaclass of the class on top of the stack (after the function is popped
|
||||||
|
// off) and remains after this.
|
||||||
CODE_METHOD_CTOR,
|
CODE_METHOD_CTOR,
|
||||||
|
|
||||||
// Create a new list with [arg] elements. The top [arg] values on the stack
|
// Create a new list with [arg] elements. The top [arg] values on the stack
|
||||||
@ -115,6 +119,19 @@ typedef enum
|
|||||||
CODE_END
|
CODE_END
|
||||||
} Code;
|
} 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
|
typedef struct
|
||||||
{
|
{
|
||||||
// TODO(bob): Make this dynamically sized.
|
// TODO(bob): Make this dynamically sized.
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user