diff --git a/chunk.h b/chunk.h index 0be5109..e070155 100644 --- a/chunk.h +++ b/chunk.h @@ -6,7 +6,7 @@ typedef enum { OP_ARRAY, OP_CONSTANT, OP_CONSTANT_LONG, OP_HASH, OP_NIL, OP_TRUE, OP_FALSE, - OP_POP, OP_GET_LOCAL, OP_GET_LOCAL_LONG, OP_INDEX, OP_SET_LOCAL, + OP_POP, OP_GET_LOCAL, OP_GET_LOCAL_LONG, OP_INDEX, OP_INDEX_FLIPPED, OP_SET_INDEX, OP_SET_LOCAL, OP_SET_LOCAL_LONG, OP_GET_GLOBAL, OP_GET_GLOBAL_LONG, OP_DEFINE_GLOBAL, OP_SET_GLOBAL, OP_SET_GLOBAL_LONG, OP_DEFINE_GLOBAL_LONG, OP_EQUAL, OP_GREATER, OP_LESS, OP_ADD, OP_SUBTRACT, OP_MULTIPLY, OP_DIVIDE, OP_NOT, diff --git a/compiler.c b/compiler.c index aca0554..1d24ad0 100644 --- a/compiler.c +++ b/compiler.c @@ -364,8 +364,18 @@ static void declareVariable() { addLocal(*name); } +static void compiler_index(bool canAssign) { + expression(); + emitByte(OP_INDEX); + if (check(TOKEN_COMMA)) { + errorAtCurrent("Cannot get multiple values at once"); + } + consume(TOKEN_RIGHT_BRACKET, "Unterminated index"); +} + static void namedVariable(Token name, bool canAssign) { uint8_t getOp, getLongOp, setOp, setLongOp; + bool indexed = false; int arg = resolveLocal(current, &name); if (arg != -1) { getOp = OP_GET_LOCAL; @@ -379,10 +389,24 @@ static void namedVariable(Token name, bool canAssign) { setOp = OP_SET_GLOBAL; setLongOp = OP_SET_GLOBAL_LONG; } - + if (canAssign && match(TOKEN_LEFT_BRACKET)) { + expression(); + consume(TOKEN_RIGHT_BRACKET, "Unterminated index"); + indexed = true; + } if (canAssign && match(TOKEN_EQUAL)) { expression(); - if (arg < 256) { + if (indexed) { + if (arg < 256) { + emitBytes(getOp, arg); + } else { + emitByte(getLongOp); + emitByte(arg & 0xFF0000 >> 16); + emitByte(arg & 0xFF00 >> 8); + emitByte(arg & 0xFF); + } + emitByte(OP_SET_INDEX); + } else if (arg < 256) { emitBytes(setOp, arg); } else { emitByte(setLongOp); @@ -399,6 +423,9 @@ static void namedVariable(Token name, bool canAssign) { emitByte(arg & 0xFF00 >> 8); emitByte(arg & 0xFF); } + if (indexed) { + emitByte(OP_INDEX_FLIPPED); + } } } @@ -415,7 +442,7 @@ static void and_(bool canAssign) { patchJump(endJump); } -static void array_unary(bool canAssign) { +static void array(bool canAssign) { expression(); if (check(TOKEN_COMMA)) { int numValues = 0; @@ -430,23 +457,13 @@ static void array_unary(bool canAssign) { emitByte(OP_ARRAY); emitByte(numValues + 1); } else { - expression(); emitByte(OP_ARRAY); emitByte(1); } consume(TOKEN_RIGHT_BRACKET, "Unterminated array"); } -static void compiler_index(bool canAssign) { - expression(); - emitByte(OP_INDEX); - if (check(TOKEN_COMMA)) { - errorAtCurrent("Cannot get multiple values at once"); - } - consume(TOKEN_RIGHT_BRACKET, "Unterminated index"); -} - -static void hash_unary(bool canAssign) { +static void hash(bool canAssign) { expression(); if (check(TOKEN_ROCKET)) { int numValues = 0; @@ -466,6 +483,8 @@ static void hash_unary(bool canAssign) { emitByte(OP_HASH); emitByte(numValues + 1); } else { + consume(TOKEN_ROCKET, "=> must follow every key in a hash"); + expression(); emitByte(OP_HASH); emitByte(1); } @@ -474,9 +493,9 @@ static void hash_unary(bool canAssign) { ParseRule rules[] = { { grouping, NULL, PREC_CALL }, // TOKEN_LEFT_PAREN { NULL, NULL, PREC_NONE }, // TOKEN_RIGHT_PAREN -{ hash_unary, NULL, PREC_CALL }, // TOKEN_LEFT_BRACE +{ hash, NULL, PREC_CALL }, // TOKEN_LEFT_BRACE { NULL, NULL, PREC_NONE }, // TOKEN_RIGHT_BRACE -{ array_unary, compiler_index, PREC_CALL }, // TOKEN_LEFT_BRACKET +{ array, compiler_index, PREC_CALL }, // TOKEN_LEFT_BRACKET { NULL, NULL, PREC_NONE }, // TOKEN_RIGHT_BRACKET { NULL, NULL, PREC_NONE }, // TOKEN_COMMA { NULL, NULL, PREC_CALL }, // TOKEN_DOT diff --git a/debug.c b/debug.c index 06892a7..a6e0ba5 100644 --- a/debug.c +++ b/debug.c @@ -87,6 +87,10 @@ int disassembleInstruction(Chunk* chunk, int offset) { return simpleInstruction("OP_POP", offset); case OP_INDEX: return simpleInstruction("OP_INDEX", offset); + case OP_INDEX_FLIPPED: + return simpleInstruction("OP_INDEX_FLIPPED", offset); + case OP_SET_INDEX: + return simpleInstruction("OP_SET_INDEX", offset); case OP_GET_LOCAL: return byteInstruction("OP_GET_LOCAL", chunk, offset); case OP_SET_LOCAL: diff --git a/vm.c b/vm.c index 125cf73..890fad9 100644 --- a/vm.c +++ b/vm.c @@ -110,6 +110,62 @@ static inline Value readConstantLong() { return vm.chunk->constants.values[constant]; } +static bool check_array_index(Value index, int* idx) { + if (!IS_NUMBER(index)) { + runtimeError("Array index must be a non-negative integer."); + return false; + } + double i_double = AS_NUMBER(index); + if (i_double < 0) { + runtimeError("Array index must be a non-negative integer."); + return false; + } + if ((int) i_double != i_double) { + runtimeError("Array index must be a non-negative integer."); + return false; + } + *idx = (int) i_double; + return true; +} + +static bool vm_index(Value index, Value val) { + if (IS_OBJ(val)) { + Obj* obj = AS_OBJ(val); + switch (obj->type) { + case OBJ_ARRAY: { + ValueArray* valArray = AS_VARRAY(val); + int i; + if (!check_array_index(index, &i)) { + return false; + } + if (i>=valArray->count) { + runtimeError("Array index out of bounds"); + return false; + } + push(valArray->values[i]); + break; + } + case OBJ_HASH: { + Table* hashTable = AS_HASH(val); + Value* value = malloc(sizeof(Value)); + if (tableGet(hashTable, index, value)) { + push(*value); + } else { + push(NIL_VAL); + } + break; + } + default: + runtimeError("Cannot index value."); + return false; + } + } else { + runtimeError("Cannot index value."); + return false; + } + return true; +} + static InterpretResult run() { for (;;) { #ifdef DEBUG_TRACE_EXECUTION @@ -184,40 +240,45 @@ static InterpretResult run() { case OP_INDEX: { Value index = pop(); Value val = pop(); + bool res = vm_index(index, val); + if (!res) return INTERPRET_RUNTIME_ERROR; + break; + } + case OP_INDEX_FLIPPED: { + Value val = pop(); + Value index = pop(); + bool res = vm_index(index, val); + if (!res) return INTERPRET_RUNTIME_ERROR; + break; + } + case OP_SET_INDEX: { + Value val = pop(); + Value set_val = pop(); + Value index = pop(); if (IS_OBJ(val)) { Obj* obj = AS_OBJ(val); switch (obj->type) { case OBJ_ARRAY: { ValueArray* valArray = AS_VARRAY(val); - if (!IS_NUMBER(index)) { - runtimeError("Array index must be a non-negative integer."); + int i; + if (!check_array_index(index, &i)) { + printf("ERR\n"); return INTERPRET_RUNTIME_ERROR; } - double i_double = AS_NUMBER(index); - if (i_double < 0) { - runtimeError("Array index must be a non-negative integer."); - return INTERPRET_RUNTIME_ERROR; + if (i>=valArray->count) { + int num_nils=i-valArray->count; + for (;num_nils>0;num_nils--) { + writeValueArray(valArray,NIL_VAL); + } + writeValueArray(valArray,set_val); + } else { + valArray->values[i]=set_val; } - if ((int) i_double != i_double) { - runtimeError("Array index must be a non-negative integer."); - return INTERPRET_RUNTIME_ERROR; - } - int i = (int) i_double; - if (i > valArray->count - 1) { - runtimeError("Array index out of bounds."); - return INTERPRET_RUNTIME_ERROR; - } - push(valArray->values[i]); break; } case OBJ_HASH: { Table* hashTable = AS_HASH(val); - Value* value = malloc(sizeof(Value)); - if (tableGet(hashTable, index, value)) { - push(*value); - } else { - push(NIL_VAL); - } + tableSet(hashTable, index, set_val); break; } default: @@ -228,6 +289,7 @@ static InterpretResult run() { runtimeError("Cannot index value."); return INTERPRET_RUNTIME_ERROR; } + push(set_val); break; } case OP_GET_LOCAL: { @@ -370,7 +432,7 @@ static InterpretResult run() { break; } } - return INTERPRET_RUNTIME_ERROR; + return INTERPRET_OK; } InterpretResult interpret(const char* source, bool repl) {