eliminate arbitrary size restrictions where possible

This commit is contained in:
K Lange 2020-12-29 06:13:30 +09:00
parent 5c8d8942b1
commit 9fd9d81afe
4 changed files with 114 additions and 80 deletions

35
chunk.h
View File

@ -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;
/**

View File

@ -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 = &current->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;
}

18
debug.c
View File

@ -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;

92
vm.c
View File

@ -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();