diff --git a/chunk.h b/chunk.h index fd8a366..6c13b82 100644 --- a/chunk.h +++ b/chunk.h @@ -11,8 +11,7 @@ * our needs. Most of the interesting changes happen in the compiler. */ typedef enum { - OP_CONSTANT, - OP_CONSTANT_LONG, + OP_CONSTANT = 1, OP_NEGATE, OP_RETURN, OP_ADD, @@ -30,42 +29,46 @@ typedef enum { OP_LESS, OP_PRINT, OP_DEFINE_GLOBAL, - OP_DEFINE_GLOBAL_LONG, OP_GET_GLOBAL, - OP_GET_GLOBAL_LONG, OP_SET_GLOBAL, - OP_SET_GLOBAL_LONG, OP_SET_LOCAL, - OP_SET_LOCAL_LONG, OP_GET_LOCAL, - OP_GET_LOCAL_LONG, OP_JUMP_IF_FALSE, OP_JUMP_IF_TRUE, OP_JUMP, OP_LOOP, OP_CALL, OP_CLOSURE, - OP_CLOSURE_LONG, OP_GET_UPVALUE, - OP_GET_UPVALUE_LONG, OP_SET_UPVALUE, - OP_SET_UPVALUE_LONG, OP_CLOSE_UPVALUE, OP_CLASS, - OP_CLASS_LONG, OP_SET_PROPERTY, - OP_SET_PROPERTY_LONG, OP_GET_PROPERTY, - OP_GET_PROPERTY_LONG, OP_METHOD, - OP_METHOD_LONG, OP_IMPORT, - OP_IMPORT_LONG, OP_INHERIT, OP_GET_SUPER, - OP_GET_SUPER_LONG, OP_PUSH_TRY, OP_RAISE, + + OP_CONSTANT_LONG = 128, + OP_PRINT_LONG, + OP_DEFINE_GLOBAL_LONG, + OP_GET_GLOBAL_LONG, + OP_SET_GLOBAL_LONG, + OP_SET_LOCAL_LONG, + OP_GET_LOCAL_LONG, + OP_CALL_LONG, + OP_CLOSURE_LONG, + OP_GET_UPVALUE_LONG, + OP_SET_UPVALUE_LONG, + OP_CLASS_LONG, + OP_SET_PROPERTY_LONG, + OP_GET_PROPERTY_LONG, + OP_METHOD_LONG, + OP_IMPORT_LONG, + OP_GET_SUPER_LONG, } KrkOpCode; /** diff --git a/compiler.c b/compiler.c index 069281f..4377cea 100644 --- a/compiler.c +++ b/compiler.c @@ -64,10 +64,12 @@ typedef struct Compiler { struct Compiler * enclosing; KrkFunction * function; FunctionType type; - Local locals[MAX_LOCALS]; size_t localCount; size_t scopeDepth; - Upvalue upvalues[MAX_LOCALS]; + size_t localsSpace; + Local * locals; + size_t upvaluesSpace; + Upvalue * upvalues; } Compiler; typedef struct ClassCompiler { @@ -94,6 +96,10 @@ static void initCompiler(Compiler * compiler, FunctionType type) { compiler->localCount = 0; compiler->scopeDepth = 0; compiler->function = krk_newFunction(); + compiler->localsSpace = 8; + compiler->locals = GROW_ARRAY(Local,NULL,0,8); + compiler->upvaluesSpace = 0; + compiler->upvalues = NULL; current = compiler; if (type != TYPE_MODULE) { @@ -255,6 +261,11 @@ static KrkFunction * endCompiler() { return function; } +static void freeCompiler(Compiler * compiler) { + FREE_ARRAY(Local,compiler->locals, compiler->localsSpace); + FREE_ARRAY(Upvalue,compiler->upvalues, compiler->upvaluesSpace); +} + static void endOfLine() { if (!(match(TOKEN_EOL) || match(TOKEN_EOF))) { errorAtCurrent("Expected end of line."); @@ -380,8 +391,12 @@ static void varDeclaration() { } static void printStatement() { - expression(); - emitByte(OP_PRINT); + int argCount = 0; + do { + expression(); + argCount++; + } while (match(TOKEN_COMMA)); + EMIT_CONSTANT_OP(OP_PRINT, argCount); } static void synchronize() { @@ -499,10 +514,8 @@ static void function(FunctionType type, size_t blockWidth) { if (match(TOKEN_EQUAL)) { consume(TOKEN_NONE,"Optional arguments can only be assigned the default value of None."); current->function->defaultArgs++; - if (current->function->defaultArgs > 255) errorAtCurrent("too many function parameters"); } else { current->function->requiredArgs++; - if (current->function->requiredArgs > 255) errorAtCurrent("too many function parameters"); } } while (match(TOKEN_COMMA)); } @@ -518,8 +531,13 @@ static void function(FunctionType type, size_t blockWidth) { for (size_t i = 0; i < function->upvalueCount; ++i) { /* TODO: if the maximum count changes, fix the sizes for this */ emitByte(compiler.upvalues[i].isLocal ? 1 : 0); - emitByte(compiler.upvalues[i].index); + if (i > 255) { + emitByte((compiler.upvalues[i].index >> 16) & 0xFF); + emitByte((compiler.upvalues[i].index >> 8) & 0xFF); + } + emitByte((compiler.upvalues[i].index) & 0xFF); } + freeCompiler(&compiler); } static void method(size_t blockWidth) { @@ -1010,9 +1028,10 @@ static size_t addUpvalue(Compiler * compiler, ssize_t index, int isLocal) { return i; } } - if (upvalueCount == MAX_LOCALS) { - error("Too many closure variables in function."); - return 0; + if (upvalueCount + 1 > current->upvaluesSpace) { + size_t old = current->upvaluesSpace; + current->upvaluesSpace = GROW_CAPACITY(old); + current->upvalues = GROW_ARRAY(Upvalue,current->upvalues,old,current->upvaluesSpace); } compiler->upvalues[upvalueCount].isLocal = isLocal; compiler->upvalues[upvalueCount].index = index; @@ -1091,7 +1110,6 @@ static void list(int canAssign) { do { expression(); /* TOKEN_FOR for comprehensions? */ - if (argCount == 255) error("Too many values in list expression."); argCount++; } while (match(TOKEN_COMMA)); } @@ -1211,9 +1229,10 @@ static ssize_t resolveLocal(Compiler * compiler, KrkToken * name) { } static void addLocal(KrkToken name) { - if (current->localCount == MAX_LOCALS) { - error("too many locals"); - return; + if (current->localCount + 1 > current->localsSpace) { + size_t old = current->localsSpace; + current->localsSpace = GROW_CAPACITY(old); + current->locals = GROW_ARRAY(Local,current->locals,old,current->localsSpace); } Local * local = ¤t->locals[current->localCount++]; local->name = name; @@ -1256,7 +1275,6 @@ static uint8_t argumentList() { if (!check(TOKEN_RIGHT_PAREN)) { do { expression(); - if (argCount == 255) error("Too many arguments to function."); // Need long call... argCount++; } while (match(TOKEN_COMMA)); } @@ -1304,6 +1322,7 @@ KrkFunction * krk_compile(const char * src, int newScope, char * fileName) { } KrkFunction * function = endCompiler(); + freeCompiler(&compiler); return parser.hadError ? NULL : function; } diff --git a/debug.c b/debug.c index cd35398..5eabf31 100644 --- a/debug.c +++ b/debug.c @@ -27,7 +27,7 @@ void krk_disassembleChunk(KrkChunk * chunk, const char * name) { #define OPERANDB(opc) case opc: { uint32_t operand = chunk->code[offset + 1]; \ fprintf(stderr, "%-16s %4d\n", #opc, (int)operand); \ return offset + 2; } -#define OPERANDL(opc) OPERANDB(opc) \ +#define OPERAND(opc) OPERANDB(opc) \ case opc ## _LONG: { uint32_t operand = (chunk->code[offset + 1] << 16) | \ (chunk->code[offset + 2] << 8) | (chunk->code[offset + 3]); \ fprintf(stderr, "%-16s %4d\n", #opc "_LONG", (int)operand); \ @@ -70,9 +70,10 @@ size_t krk_disassembleInstruction(KrkChunk * chunk, size_t offset) { SIMPLE(OP_EQUAL) SIMPLE(OP_GREATER) SIMPLE(OP_LESS) - SIMPLE(OP_PRINT) SIMPLE(OP_POP) SIMPLE(OP_INHERIT) + SIMPLE(OP_RAISE) + SIMPLE(OP_CLOSE_UPVALUE) CONSTANT(OP_DEFINE_GLOBAL,(void)0) CONSTANT(OP_CONSTANT,(void)0) CONSTANT(OP_GET_GLOBAL,(void)0) @@ -84,18 +85,17 @@ size_t krk_disassembleInstruction(KrkChunk * chunk, size_t offset) { CONSTANT(OP_CLOSURE, CLOSURE_MORE) CONSTANT(OP_IMPORT, (void)0) CONSTANT(OP_GET_SUPER, (void)0) - OPERANDL(OP_SET_LOCAL) - OPERANDL(OP_GET_LOCAL) - OPERANDL(OP_SET_UPVALUE) - OPERANDL(OP_GET_UPVALUE) + OPERAND(OP_SET_LOCAL) + OPERAND(OP_GET_LOCAL) + OPERAND(OP_SET_UPVALUE) + OPERAND(OP_GET_UPVALUE) + OPERAND(OP_CALL) + OPERAND(OP_PRINT) JUMP(OP_JUMP,+) JUMP(OP_JUMP_IF_FALSE,+) JUMP(OP_JUMP_IF_TRUE,+) JUMP(OP_LOOP,-) JUMP(OP_PUSH_TRY,+) - SIMPLE(OP_RAISE) - OPERANDB(OP_CALL) - SIMPLE(OP_CLOSE_UPVALUE) default: fprintf(stderr, "Unknown opcode: %02x\n", opcode); return offset + 1; diff --git a/vm.c b/vm.c index abaf2ac..67579a0 100644 --- a/vm.c +++ b/vm.c @@ -729,21 +729,13 @@ static void addObjects() { #define READ_CONSTANT(s) (frame->closure->function->chunk.constants.values[readBytes(frame,s)]) #define READ_STRING(s) AS_STRING(READ_CONSTANT(s)) -static size_t readBytes(CallFrame * frame, int num) { - if (num == 1) return READ_BYTE(); - else if (num == 2) { - unsigned int top = READ_BYTE(); - unsigned int bot = READ_BYTE(); - return (top << 8) | (bot); - } else if (num == 3) { - unsigned int top = READ_BYTE(); - unsigned int mid = READ_BYTE(); - unsigned int bot = READ_BYTE(); - return (top << 16) | (mid << 8) | (bot); +static inline size_t readBytes(CallFrame * frame, int num) { + size_t out = READ_BYTE(); + while (--num) { + out <<= 8; + out |= (READ_BYTE() & 0xFF); } - - krk_runtimeError("Invalid byte read?"); - return (size_t)-1; + return out; } static KrkClosure * boundNative(NativeFn method, int arity) { @@ -764,8 +756,16 @@ static KrkClosure * boundNative(NativeFn method, int arity) { } /* Call with these arguments */ - krk_writeChunk(&methodWrapper->chunk, OP_CALL, 1); - krk_writeChunk(&methodWrapper->chunk, arity + 1, 1); /* arguments to call with */ + if (arity > 255) { + int n = arity + 1; + krk_writeChunk(&methodWrapper->chunk, OP_CALL_LONG, 1); + krk_writeChunk(&methodWrapper->chunk, (n >> 16) & 0xFF, 1); + krk_writeChunk(&methodWrapper->chunk, (n >> 8) & 0xFF, 1); + krk_writeChunk(&methodWrapper->chunk, (n >> 0) & 0xFF, 1); + } else { + krk_writeChunk(&methodWrapper->chunk, OP_CALL, 1); + krk_writeChunk(&methodWrapper->chunk, arity + 1, 1); /* arguments to call with */ + } /* Return from the wrapper with whatever result we got from the native method */ krk_writeChunk(&methodWrapper->chunk, OP_RETURN, 1); @@ -898,15 +898,26 @@ static KrkValue run() { (size_t)(frame->ip - frame->closure->function->chunk.code)); } #endif - uint8_t opcode; - switch ((opcode = READ_BYTE())) { + uint8_t opcode = READ_BYTE(); + int operandWidth = (opcode & (1 << 7)) ? 3 : 1; + + switch (opcode) { + case OP_PRINT_LONG: case OP_PRINT: { - if (!IS_STRING(krk_peek(0))) { - krk_push(OBJECT_VAL(S(""))); - krk_swap(); - addObjects(); + uint32_t args = readBytes(frame, operandWidth); + for (uint32_t i = 0; i < args; ++i) { + KrkValue printable = krk_peek(args-i-1); + if (!IS_STRING(printable)) { + krk_push(OBJECT_VAL(S(""))); + krk_push(printable); + addObjects(); + printable = krk_pop(); + } + fprintf(stdout, "%s%s", AS_CSTRING(printable), (i == args - 1) ? "\n" : " "); + } + for (uint32_t i = 0; i < args; ++i) { + krk_pop(); } - fprintf(stdout, "%s\n", AS_CSTRING(krk_pop())); break; } case OP_RETURN: { @@ -950,7 +961,7 @@ static KrkValue run() { } case OP_CONSTANT_LONG: case OP_CONSTANT: { - size_t index = readBytes(frame, opcode == OP_CONSTANT ? 1 : 3); + size_t index = readBytes(frame, operandWidth); KrkValue constant = frame->closure->function->chunk.constants.values[index]; krk_push(constant); break; @@ -962,14 +973,14 @@ static KrkValue run() { case OP_POP: krk_pop(); break; case OP_DEFINE_GLOBAL_LONG: case OP_DEFINE_GLOBAL: { - KrkString * name = READ_STRING((opcode == OP_DEFINE_GLOBAL ? 1 : 3)); + KrkString * name = READ_STRING(operandWidth); krk_tableSet(&vm.globals, OBJECT_VAL(name), krk_peek(0)); krk_pop(); break; } case OP_GET_GLOBAL_LONG: case OP_GET_GLOBAL: { - KrkString * name = READ_STRING((opcode == OP_GET_GLOBAL ? 1 : 3)); + KrkString * name = READ_STRING(operandWidth); KrkValue value; if (!krk_tableGet(&vm.globals, OBJECT_VAL(name), &value)) { krk_runtimeError("Undefined variable '%s'.", name->chars); @@ -980,7 +991,7 @@ static KrkValue run() { } case OP_SET_GLOBAL_LONG: case OP_SET_GLOBAL: { - KrkString * name = READ_STRING((opcode == OP_SET_GLOBAL ? 1 : 3)); + KrkString * name = READ_STRING(operandWidth); if (krk_tableSet(&vm.globals, OBJECT_VAL(name), krk_peek(0))) { krk_tableDelete(&vm.globals, OBJECT_VAL(name)); /* TODO: This should probably just work as an assignment? */ @@ -991,7 +1002,7 @@ static KrkValue run() { } case OP_IMPORT_LONG: case OP_IMPORT: { - KrkString * name = READ_STRING((opcode == OP_IMPORT ? 1 : 3)); + KrkString * name = READ_STRING(operandWidth); KrkValue module; if (!krk_tableGet(&vm.modules, OBJECT_VAL(name), &module)) { /* Try to open it */ @@ -1014,13 +1025,13 @@ static KrkValue run() { } case OP_GET_LOCAL_LONG: case OP_GET_LOCAL: { - uint32_t slot = readBytes(frame, (opcode == OP_GET_LOCAL ? 1 : 3)); + uint32_t slot = readBytes(frame, operandWidth); krk_push(vm.stack[frame->slots + slot]); break; } case OP_SET_LOCAL_LONG: case OP_SET_LOCAL: { - uint32_t slot = readBytes(frame, (opcode == OP_SET_LOCAL ? 1 : 3)); + uint32_t slot = readBytes(frame, operandWidth); vm.stack[frame->slots + slot] = krk_peek(0); break; } @@ -1054,8 +1065,9 @@ static KrkValue run() { vm.flags |= KRK_HAS_EXCEPTION; goto _finishException; } + case OP_CALL_LONG: case OP_CALL: { - int argCount = READ_BYTE(); + int argCount = readBytes(frame, operandWidth); if (!callValue(krk_peek(argCount), argCount)) { if (vm.flags & KRK_HAS_EXCEPTION) goto _finishException; return NONE_VAL(); @@ -1065,12 +1077,12 @@ static KrkValue run() { } case OP_CLOSURE_LONG: case OP_CLOSURE: { - KrkFunction * function = AS_FUNCTION(READ_CONSTANT((opcode == OP_CLOSURE ? 1 : 3))); + KrkFunction * function = AS_FUNCTION(READ_CONSTANT(operandWidth)); KrkClosure * closure = krk_newClosure(function); krk_push(OBJECT_VAL(closure)); for (size_t i = 0; i < closure->upvalueCount; ++i) { int isLocal = READ_BYTE(); - int index = READ_BYTE(); + int index = readBytes(frame,(i > 255) ? 3 : 1); if (isLocal) { closure->upvalues[i] = captureUpvalue(frame->slots + index); } else { @@ -1081,13 +1093,13 @@ static KrkValue run() { } case OP_GET_UPVALUE_LONG: case OP_GET_UPVALUE: { - int slot = readBytes(frame, (opcode == OP_GET_UPVALUE) ? 1 : 3); + int slot = readBytes(frame, operandWidth); krk_push(*UPVALUE_LOCATION(frame->closure->upvalues[slot])); break; } case OP_SET_UPVALUE_LONG: case OP_SET_UPVALUE: { - int slot = readBytes(frame, (opcode == OP_SET_UPVALUE) ? 1 : 3); + int slot = readBytes(frame, operandWidth); *UPVALUE_LOCATION(frame->closure->upvalues[slot]) = krk_peek(0); break; } @@ -1097,7 +1109,7 @@ static KrkValue run() { break; case OP_CLASS_LONG: case OP_CLASS: { - KrkString * name = READ_STRING((opcode == OP_CLASS ? 1 : 3)); + KrkString * name = READ_STRING(operandWidth); KrkClass * _class = krk_newClass(name); _class->filename = frame->closure->function->chunk.filename; krk_tableAddAll(&vm.object_class->methods, &_class->methods); @@ -1106,7 +1118,7 @@ static KrkValue run() { } case OP_GET_PROPERTY_LONG: case OP_GET_PROPERTY: { - KrkString * name = READ_STRING((opcode == OP_GET_PROPERTY ? 1 : 3)); + KrkString * name = READ_STRING(operandWidth); switch (krk_peek(0).type) { case VAL_OBJECT: switch (OBJECT_TYPE(krk_peek(0))) { @@ -1196,7 +1208,7 @@ _undefined: goto _finishException; } KrkInstance * instance = AS_INSTANCE(krk_peek(1)); - KrkString * name = READ_STRING((opcode == OP_SET_PROPERTY ? 1 : 3)); + KrkString * name = READ_STRING(operandWidth); krk_tableSet(&instance->fields, OBJECT_VAL(name), krk_peek(0)); KrkValue value = krk_pop(); krk_pop(); /* instance */ @@ -1205,7 +1217,7 @@ _undefined: } case OP_METHOD_LONG: case OP_METHOD: { - defineMethod(READ_STRING((opcode == OP_METHOD ? 1 : 3))); + defineMethod(READ_STRING(operandWidth)); break; } case OP_INHERIT: { @@ -1221,7 +1233,7 @@ _undefined: } case OP_GET_SUPER_LONG: case OP_GET_SUPER: { - KrkString * name = READ_STRING((opcode == OP_GET_SUPER ? 1 : 3)); + KrkString * name = READ_STRING(operandWidth); KrkClass * superclass = AS_CLASS(krk_pop()); if (!bindMethod(superclass, name)) { return NONE_VAL();