diff --git a/src/compiler.c b/src/compiler.c index 79e4fc0a..a016ef1e 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -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) diff --git a/src/compiler.h b/src/compiler.h index 98c44b4c..2f79b0cf 100644 --- a/src/compiler.h +++ b/src/compiler.h @@ -3,6 +3,6 @@ #include "vm.h" -ObjBlock* compile(VM* vm, const char* source); +ObjFn* compile(VM* vm, const char* source); #endif diff --git a/src/main.c b/src/main.c index 410bbd29..94b5a4d5 100644 --- a/src/main.c +++ b/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"); diff --git a/src/primitives.c b/src/primitives.c index f2db2ee4..22c64cc7 100644 --- a/src/primitives.c +++ b/src/primitives.c @@ -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(); diff --git a/src/vm.c b/src/vm.c index d801437d..56ffa97f 100644 --- a/src/vm.c +++ b/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; diff --git a/src/vm.h b/src/vm.h index 4cb4d801..178f8a96 100644 --- a/src/vm.h +++ b/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);