mirror of
https://github.com/wren-lang/wren.git
synced 2026-01-11 22:28:45 +01:00
"block" -> "function".
Moving away from using Smalltalk-style block objects for all scopes.
This commit is contained in:
@ -103,8 +103,8 @@ typedef struct sCompiler
|
||||
// top level.
|
||||
struct sCompiler* parent;
|
||||
|
||||
// The block being compiled.
|
||||
ObjBlock* block;
|
||||
// The function being compiled.
|
||||
ObjFn* fn;
|
||||
int numCodes;
|
||||
|
||||
// Symbol table for declared local variables in this block.
|
||||
@ -117,7 +117,6 @@ enum
|
||||
{
|
||||
PREC_NONE,
|
||||
PREC_LOWEST,
|
||||
|
||||
PREC_EQUALITY, // == !=
|
||||
PREC_COMPARISON, // < > <= >=
|
||||
PREC_BITWISE, // | &
|
||||
@ -136,7 +135,7 @@ typedef struct
|
||||
|
||||
static void initCompiler(Compiler* compiler, Parser* parser, Compiler* parent);
|
||||
|
||||
static ObjBlock* compileBlock(Parser* parser, Compiler* parent,
|
||||
static ObjFn* compileFunction(Parser* parser, Compiler* parent,
|
||||
TokenType endToken);
|
||||
|
||||
static int addConstant(Compiler* compiler, Value constant);
|
||||
@ -272,7 +271,7 @@ ParseRule rules[] =
|
||||
/* TOKEN_EOF */ UNUSED
|
||||
};
|
||||
|
||||
ObjBlock* compile(VM* vm, const char* source)
|
||||
ObjFn* compile(VM* vm, const char* source)
|
||||
{
|
||||
Parser parser;
|
||||
parser.vm = vm;
|
||||
@ -296,7 +295,7 @@ ObjBlock* compile(VM* vm, const char* source)
|
||||
// Read the first token.
|
||||
nextToken(&parser);
|
||||
|
||||
return compileBlock(&parser, NULL, TOKEN_EOF);
|
||||
return compileFunction(&parser, NULL, TOKEN_EOF);
|
||||
}
|
||||
|
||||
void initCompiler(Compiler* compiler, Parser* parser,
|
||||
@ -307,16 +306,16 @@ void initCompiler(Compiler* compiler, Parser* parser,
|
||||
compiler->numCodes = 0;
|
||||
initSymbolTable(&compiler->locals);
|
||||
|
||||
compiler->block = makeBlock();
|
||||
compiler->fn = makeFunction();
|
||||
// TODO(bob): Hack! make variable sized.
|
||||
compiler->block->bytecode = malloc(sizeof(Code) * 1024);
|
||||
compiler->fn->bytecode = malloc(sizeof(Code) * 1024);
|
||||
|
||||
// TODO(bob): Hack! make variable sized.
|
||||
compiler->block->constants = malloc(sizeof(Value) * 256);
|
||||
compiler->block->numConstants = 0;
|
||||
compiler->fn->constants = malloc(sizeof(Value) * 256);
|
||||
compiler->fn->numConstants = 0;
|
||||
}
|
||||
|
||||
ObjBlock* compileBlock(Parser* parser, Compiler* parent, TokenType endToken)
|
||||
ObjFn* compileFunction(Parser* parser, Compiler* parent, TokenType endToken)
|
||||
{
|
||||
Compiler compiler;
|
||||
initCompiler(&compiler, parser, parent);
|
||||
@ -340,15 +339,15 @@ ObjBlock* compileBlock(Parser* parser, Compiler* parent, TokenType endToken)
|
||||
|
||||
emit(&compiler, CODE_END);
|
||||
|
||||
compiler.block->numLocals = compiler.locals.count;
|
||||
compiler.fn->numLocals = compiler.locals.count;
|
||||
|
||||
return parser->hasError ? NULL : compiler.block;
|
||||
return parser->hasError ? NULL : compiler.fn;
|
||||
}
|
||||
|
||||
int addConstant(Compiler* compiler, Value constant)
|
||||
{
|
||||
compiler->block->constants[compiler->block->numConstants++] = constant;
|
||||
return compiler->block->numConstants - 1;
|
||||
compiler->fn->constants[compiler->fn->numConstants++] = constant;
|
||||
return compiler->fn->numConstants - 1;
|
||||
}
|
||||
|
||||
int defineName(Compiler* compiler)
|
||||
@ -394,7 +393,7 @@ int internSymbol(Compiler* compiler)
|
||||
|
||||
int emit(Compiler* compiler, Code code)
|
||||
{
|
||||
compiler->block->bytecode[compiler->numCodes++] = code;
|
||||
compiler->fn->bytecode[compiler->numCodes++] = code;
|
||||
return compiler->numCodes - 1;
|
||||
}
|
||||
|
||||
@ -441,7 +440,7 @@ void statement(Compiler* compiler)
|
||||
int symbol = internSymbol(compiler);
|
||||
|
||||
consume(compiler, TOKEN_LEFT_BRACE);
|
||||
ObjBlock* method = compileBlock(compiler->parser, compiler,
|
||||
ObjFn* method = compileFunction(compiler->parser, compiler,
|
||||
TOKEN_RIGHT_BRACE);
|
||||
consume(compiler, TOKEN_LINE);
|
||||
|
||||
@ -500,7 +499,7 @@ void expression(Compiler* compiler)
|
||||
int elseJump = emit(compiler, 255);
|
||||
|
||||
// Patch the jump.
|
||||
compiler->block->bytecode[ifJump] = compiler->numCodes - ifJump - 1;
|
||||
compiler->fn->bytecode[ifJump] = compiler->numCodes - ifJump - 1;
|
||||
|
||||
// Compile the else branch if there is one.
|
||||
if (match(compiler, TOKEN_ELSE))
|
||||
@ -515,7 +514,7 @@ void expression(Compiler* compiler)
|
||||
}
|
||||
|
||||
// Patch the jump over the else.
|
||||
compiler->block->bytecode[elseJump] = compiler->numCodes - elseJump - 1;
|
||||
compiler->fn->bytecode[elseJump] = compiler->numCodes - elseJump - 1;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -552,15 +551,16 @@ void grouping(Compiler* compiler)
|
||||
|
||||
void block(Compiler* compiler)
|
||||
{
|
||||
ObjBlock* block = compileBlock(
|
||||
// TODO(bob): Make this just a local scope, not a block object.
|
||||
ObjFn* fn = compileFunction(
|
||||
compiler->parser, compiler, TOKEN_RIGHT_BRACE);
|
||||
|
||||
// Add the block to the constant table.
|
||||
compiler->block->constants[compiler->block->numConstants++] = (Value)block;
|
||||
// Add the function to the constant table.
|
||||
compiler->fn->constants[compiler->fn->numConstants++] = (Value)fn;
|
||||
|
||||
// Compile the code to load it.
|
||||
emit(compiler, CODE_CONSTANT);
|
||||
emit(compiler, compiler->block->numConstants - 1);
|
||||
emit(compiler, compiler->fn->numConstants - 1);
|
||||
}
|
||||
|
||||
void boolean(Compiler* compiler)
|
||||
|
||||
@ -3,6 +3,6 @@
|
||||
|
||||
#include "vm.h"
|
||||
|
||||
ObjBlock* compile(VM* vm, const char* source);
|
||||
ObjFn* compile(VM* vm, const char* source);
|
||||
|
||||
#endif
|
||||
|
||||
12
src/main.c
12
src/main.c
@ -50,12 +50,12 @@ int runFile(const char* path)
|
||||
{
|
||||
char* source = readFile(path);
|
||||
VM* vm = newVM();
|
||||
ObjBlock* block = compile(vm, source);
|
||||
ObjFn* fn = compile(vm, source);
|
||||
|
||||
int exitCode = 0;
|
||||
if (block)
|
||||
if (fn != NULL)
|
||||
{
|
||||
interpret(vm, block);
|
||||
interpret(vm, fn);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -78,11 +78,11 @@ int runRepl()
|
||||
char line[MAX_LINE];
|
||||
fgets(line, MAX_LINE, stdin);
|
||||
// TODO(bob): Handle failure.
|
||||
ObjBlock* block = compile(vm, line);
|
||||
ObjFn* fn = compile(vm, line);
|
||||
|
||||
if (block != NULL)
|
||||
if (fn != NULL)
|
||||
{
|
||||
Value result = interpret(vm, block);
|
||||
Value result = interpret(vm, fn);
|
||||
printf("= ");
|
||||
printValue(result);
|
||||
printf("\n");
|
||||
|
||||
@ -22,16 +22,6 @@
|
||||
vm->globals[symbol] = (Value)obj; \
|
||||
}
|
||||
|
||||
DEF_PRIMITIVE(block_call)
|
||||
{
|
||||
// The call instruction leading to this primitive has one argument. So when
|
||||
// we push the block onto the callstack, we again use one argument. That
|
||||
// ensures that the result of evaluating the block goes into the slot that
|
||||
// the caller of *this* primitive is expecting.
|
||||
callBlock(fiber, AS_BLOCK(args[0]), 1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DEF_PRIMITIVE(bool_eqeq)
|
||||
{
|
||||
if (args[1]->type != OBJ_FALSE && args[1]->type != OBJ_TRUE)
|
||||
@ -69,6 +59,16 @@ DEF_PRIMITIVE(bool_toString)
|
||||
}
|
||||
}
|
||||
|
||||
DEF_PRIMITIVE(fn_call)
|
||||
{
|
||||
// The call instruction leading to this primitive has one argument. So when
|
||||
// we push the block onto the callstack, we again use one argument. That
|
||||
// ensures that the result of evaluating the block goes into the slot that
|
||||
// the caller of *this* primitive is expecting.
|
||||
callFunction(fiber, AS_FN(args[0]), 1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DEF_PRIMITIVE(num_abs)
|
||||
{
|
||||
double value = AS_NUM(args[0]);
|
||||
@ -217,9 +217,6 @@ DEF_PRIMITIVE(io_write)
|
||||
|
||||
void registerPrimitives(VM* vm)
|
||||
{
|
||||
vm->blockClass = makeClass();
|
||||
PRIMITIVE(vm->blockClass, "call", block_call);
|
||||
|
||||
vm->boolClass = makeClass();
|
||||
PRIMITIVE(vm->boolClass, "toString", bool_toString);
|
||||
PRIMITIVE(vm->boolClass, "== ", bool_eqeq);
|
||||
@ -227,6 +224,9 @@ void registerPrimitives(VM* vm)
|
||||
|
||||
vm->classClass = makeClass();
|
||||
|
||||
vm->fnClass = makeClass();
|
||||
PRIMITIVE(vm->fnClass, "call", fn_call);
|
||||
|
||||
vm->nullClass = makeClass();
|
||||
|
||||
vm->numClass = makeClass();
|
||||
|
||||
75
src/vm.c
75
src/vm.c
@ -38,14 +38,6 @@ Value makeBool(int value)
|
||||
return obj;
|
||||
}
|
||||
|
||||
ObjBlock* makeBlock()
|
||||
{
|
||||
ObjBlock* block = malloc(sizeof(ObjBlock));
|
||||
block->obj.type = OBJ_BLOCK;
|
||||
block->obj.flags = 0;
|
||||
return block;
|
||||
}
|
||||
|
||||
ObjClass* makeSingleClass()
|
||||
{
|
||||
ObjClass* obj = malloc(sizeof(ObjClass));
|
||||
@ -71,6 +63,14 @@ ObjClass* makeClass()
|
||||
return classObj;
|
||||
}
|
||||
|
||||
ObjFn* makeFunction()
|
||||
{
|
||||
ObjFn* fn = malloc(sizeof(ObjFn));
|
||||
fn->obj.type = OBJ_FN;
|
||||
fn->obj.flags = 0;
|
||||
return fn;
|
||||
}
|
||||
|
||||
ObjInstance* makeInstance(ObjClass* classObj)
|
||||
{
|
||||
ObjInstance* instance = malloc(sizeof(ObjInstance));
|
||||
@ -306,24 +306,24 @@ void dumpCode(ObjBlock* block)
|
||||
}
|
||||
*/
|
||||
|
||||
Value interpret(VM* vm, ObjBlock* block)
|
||||
Value interpret(VM* vm, ObjFn* fn)
|
||||
{
|
||||
Fiber fiber;
|
||||
fiber.stackSize = 0;
|
||||
fiber.numFrames = 0;
|
||||
|
||||
callBlock(&fiber, block, 0);
|
||||
callFunction(&fiber, fn, 0);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
CallFrame* frame = &fiber.frames[fiber.numFrames - 1];
|
||||
|
||||
switch (frame->block->bytecode[frame->ip++])
|
||||
switch (frame->fn->bytecode[frame->ip++])
|
||||
{
|
||||
case CODE_CONSTANT:
|
||||
{
|
||||
int constant = frame->block->bytecode[frame->ip++];
|
||||
Value value = frame->block->constants[constant];
|
||||
int constant = frame->fn->bytecode[frame->ip++];
|
||||
Value value = frame->fn->constants[constant];
|
||||
push(&fiber, value);
|
||||
break;
|
||||
}
|
||||
@ -357,40 +357,40 @@ Value interpret(VM* vm, ObjBlock* block)
|
||||
|
||||
case CODE_METHOD:
|
||||
{
|
||||
int symbol = frame->block->bytecode[frame->ip++];
|
||||
int constant = frame->block->bytecode[frame->ip++];
|
||||
int symbol = frame->fn->bytecode[frame->ip++];
|
||||
int constant = frame->fn->bytecode[frame->ip++];
|
||||
ObjClass* classObj = (ObjClass*)fiber.stack[fiber.stackSize - 1];
|
||||
|
||||
ObjBlock* body = (ObjBlock*)frame->block->constants[constant];
|
||||
ObjFn* body = AS_FN(frame->fn->constants[constant]);
|
||||
classObj->methods[symbol].type = METHOD_BLOCK;
|
||||
classObj->methods[symbol].block = body;
|
||||
classObj->methods[symbol].fn = body;
|
||||
break;
|
||||
}
|
||||
|
||||
case CODE_LOAD_LOCAL:
|
||||
{
|
||||
int local = frame->block->bytecode[frame->ip++];
|
||||
int local = frame->fn->bytecode[frame->ip++];
|
||||
push(&fiber, fiber.stack[frame->locals + local]);
|
||||
break;
|
||||
}
|
||||
|
||||
case CODE_STORE_LOCAL:
|
||||
{
|
||||
int local = frame->block->bytecode[frame->ip++];
|
||||
int local = frame->fn->bytecode[frame->ip++];
|
||||
fiber.stack[frame->locals + local] = fiber.stack[fiber.stackSize - 1];
|
||||
break;
|
||||
}
|
||||
|
||||
case CODE_LOAD_GLOBAL:
|
||||
{
|
||||
int global = frame->block->bytecode[frame->ip++];
|
||||
int global = frame->fn->bytecode[frame->ip++];
|
||||
push(&fiber, vm->globals[global]);
|
||||
break;
|
||||
}
|
||||
|
||||
case CODE_STORE_GLOBAL:
|
||||
{
|
||||
int global = frame->block->bytecode[frame->ip++];
|
||||
int global = frame->fn->bytecode[frame->ip++];
|
||||
vm->globals[global] = fiber.stack[fiber.stackSize - 1];
|
||||
break;
|
||||
}
|
||||
@ -416,18 +416,14 @@ Value interpret(VM* vm, ObjBlock* block)
|
||||
case CODE_CALL_10:
|
||||
{
|
||||
// Add one for the implicit receiver argument.
|
||||
int numArgs = frame->block->bytecode[frame->ip - 1] - CODE_CALL_0 + 1;
|
||||
int symbol = frame->block->bytecode[frame->ip++];
|
||||
int numArgs = frame->fn->bytecode[frame->ip - 1] - CODE_CALL_0 + 1;
|
||||
int symbol = frame->fn->bytecode[frame->ip++];
|
||||
|
||||
Value receiver = fiber.stack[fiber.stackSize - numArgs];
|
||||
|
||||
ObjClass* classObj;
|
||||
switch (receiver->type)
|
||||
{
|
||||
case OBJ_BLOCK:
|
||||
classObj = vm->blockClass;
|
||||
break;
|
||||
|
||||
case OBJ_CLASS:
|
||||
classObj = ((ObjClass*)receiver)->metaclass;
|
||||
break;
|
||||
@ -437,6 +433,10 @@ Value interpret(VM* vm, ObjBlock* block)
|
||||
classObj = vm->boolClass;
|
||||
break;
|
||||
|
||||
case OBJ_FN:
|
||||
classObj = vm->fnClass;
|
||||
break;
|
||||
|
||||
case OBJ_NULL:
|
||||
classObj = vm->nullClass;
|
||||
break;
|
||||
@ -484,7 +484,7 @@ Value interpret(VM* vm, ObjBlock* block)
|
||||
}
|
||||
|
||||
case METHOD_BLOCK:
|
||||
callBlock(&fiber, method->block, numArgs);
|
||||
callFunction(&fiber, method->fn, numArgs);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
@ -492,14 +492,14 @@ Value interpret(VM* vm, ObjBlock* block)
|
||||
|
||||
case CODE_JUMP:
|
||||
{
|
||||
int offset = frame->block->bytecode[frame->ip++];
|
||||
int offset = frame->fn->bytecode[frame->ip++];
|
||||
frame->ip += offset;
|
||||
break;
|
||||
}
|
||||
|
||||
case CODE_JUMP_IF:
|
||||
{
|
||||
int offset = frame->block->bytecode[frame->ip++];
|
||||
int offset = frame->fn->bytecode[frame->ip++];
|
||||
Value condition = pop(&fiber);
|
||||
|
||||
// False is the only falsey value.
|
||||
@ -530,20 +530,21 @@ Value interpret(VM* vm, ObjBlock* block)
|
||||
}
|
||||
}
|
||||
|
||||
void callBlock(Fiber* fiber, ObjBlock* block, int numArgs)
|
||||
void callFunction(Fiber* fiber, ObjFn* fn, int numArgs)
|
||||
{
|
||||
fiber->frames[fiber->numFrames].block = block;
|
||||
fiber->frames[fiber->numFrames].fn = fn;
|
||||
fiber->frames[fiber->numFrames].ip = 0;
|
||||
fiber->frames[fiber->numFrames].locals = fiber->stackSize - numArgs;
|
||||
|
||||
// Make empty slots for locals.
|
||||
// TODO(bob): Should we do this eagerly, or have the bytecode have pushnil
|
||||
// instructions before each time a local is stored for the first time?
|
||||
for (int i = 0; i < block->numLocals - numArgs; i++)
|
||||
for (int i = 0; i < fn->numLocals - numArgs; i++)
|
||||
{
|
||||
push(fiber, NULL);
|
||||
}
|
||||
|
||||
// TODO(bob): Check for stack overflow.
|
||||
fiber->numFrames++;
|
||||
}
|
||||
|
||||
@ -552,10 +553,6 @@ void printValue(Value value)
|
||||
// TODO(bob): Do more useful stuff here.
|
||||
switch (value->type)
|
||||
{
|
||||
case OBJ_BLOCK:
|
||||
printf("[block]");
|
||||
break;
|
||||
|
||||
case OBJ_CLASS:
|
||||
printf("[class]");
|
||||
break;
|
||||
@ -564,6 +561,10 @@ void printValue(Value value)
|
||||
printf("false");
|
||||
break;
|
||||
|
||||
case OBJ_FN:
|
||||
printf("[fn]");
|
||||
break;
|
||||
|
||||
case OBJ_INSTANCE:
|
||||
printf("[instance]");
|
||||
break;
|
||||
|
||||
28
src/vm.h
28
src/vm.h
@ -6,12 +6,12 @@
|
||||
#define MAX_CALL_FRAMES 256
|
||||
#define MAX_SYMBOLS 256
|
||||
|
||||
// Get the block value of [obj] (0 or 1), which must be a block.
|
||||
#define AS_BLOCK(obj) ((ObjBlock*)obj)
|
||||
|
||||
// Get the bool value of [obj] (0 or 1), which must be a boolean.
|
||||
#define AS_BOOL(obj) (((Obj*)obj)->type == OBJ_TRUE)
|
||||
|
||||
// Get the function value of [obj] (0 or 1), which must be a function.
|
||||
#define AS_FN(obj) ((ObjFn*)obj)
|
||||
|
||||
// Get the double value of [obj], which must be a number.
|
||||
#define AS_NUM(obj) (((ObjNum*)obj)->value)
|
||||
|
||||
@ -19,9 +19,9 @@
|
||||
#define AS_STRING(obj) (((ObjString*)obj)->value)
|
||||
|
||||
typedef enum {
|
||||
OBJ_BLOCK,
|
||||
OBJ_CLASS,
|
||||
OBJ_FALSE,
|
||||
OBJ_FN,
|
||||
OBJ_INSTANCE,
|
||||
OBJ_NULL,
|
||||
OBJ_NUM,
|
||||
@ -55,7 +55,7 @@ typedef struct
|
||||
Value* constants;
|
||||
int numConstants;
|
||||
int numLocals;
|
||||
} ObjBlock;
|
||||
} ObjFn;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
@ -70,7 +70,7 @@ typedef struct
|
||||
union
|
||||
{
|
||||
Primitive primitive;
|
||||
ObjBlock* block;
|
||||
ObjFn* fn;
|
||||
};
|
||||
} Method;
|
||||
|
||||
@ -175,9 +175,9 @@ struct sVM
|
||||
{
|
||||
SymbolTable symbols;
|
||||
|
||||
ObjClass* blockClass;
|
||||
ObjClass* boolClass;
|
||||
ObjClass* classClass;
|
||||
ObjClass* fnClass;
|
||||
ObjClass* nullClass;
|
||||
ObjClass* numClass;
|
||||
ObjClass* stringClass;
|
||||
@ -196,8 +196,8 @@ typedef struct
|
||||
// block's bytecode.
|
||||
int ip;
|
||||
|
||||
// The block being executed.
|
||||
ObjBlock* block;
|
||||
// The function being executed.
|
||||
ObjFn* fn;
|
||||
|
||||
// Index of the stack slot that contains the first local for this block.
|
||||
int locals;
|
||||
@ -219,9 +219,9 @@ void freeVM(VM* vm);
|
||||
// TODO(bob): Inline or macro?
|
||||
Value makeBool(int value);
|
||||
|
||||
// Creates a new block object. Assumes the compiler will fill it in with
|
||||
// Creates a new function object. Assumes the compiler will fill it in with
|
||||
// bytecode, constants, etc.
|
||||
ObjBlock* makeBlock();
|
||||
ObjFn* makeFunction();
|
||||
|
||||
// Creates a new class object.
|
||||
ObjClass* makeClass();
|
||||
@ -259,11 +259,11 @@ int findSymbol(SymbolTable* symbols, const char* name, size_t length);
|
||||
// Given an index in the symbol table, returns its name.
|
||||
const char* getSymbolName(SymbolTable* symbols, int symbol);
|
||||
|
||||
Value interpret(VM* vm, ObjBlock* block);
|
||||
Value interpret(VM* vm, ObjFn* fn);
|
||||
|
||||
// Push [block] onto [fiber]'s callstack and invoke it. Expects [numArgs]
|
||||
// Push [fn] onto [fiber]'s callstack and invoke it. Expects [numArgs]
|
||||
// arguments (including the receiver) to be on the top of the stack already.
|
||||
void callBlock(Fiber* fiber, ObjBlock* block, int numArgs);
|
||||
void callFunction(Fiber* fiber, ObjFn* fn, int numArgs);
|
||||
|
||||
void printValue(Value value);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user