Finish base features of arrays and hashes

Fix single-valued arrays and single-pair hashes, add support for
indexing variables, and add support for assigning to arrays and hashes
in variables
This commit is contained in:
pjht 2019-06-03 14:38:44 -05:00
parent 701b9aad40
commit c5b12fe8c9
4 changed files with 125 additions and 40 deletions

View File

@ -6,7 +6,7 @@
typedef enum { typedef enum {
OP_ARRAY, OP_CONSTANT, OP_CONSTANT_LONG, OP_HASH, OP_NIL, OP_TRUE, OP_FALSE, 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_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_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, OP_GREATER, OP_LESS, OP_ADD, OP_SUBTRACT, OP_MULTIPLY, OP_DIVIDE, OP_NOT,

View File

@ -364,8 +364,18 @@ static void declareVariable() {
addLocal(*name); 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) { static void namedVariable(Token name, bool canAssign) {
uint8_t getOp, getLongOp, setOp, setLongOp; uint8_t getOp, getLongOp, setOp, setLongOp;
bool indexed = false;
int arg = resolveLocal(current, &name); int arg = resolveLocal(current, &name);
if (arg != -1) { if (arg != -1) {
getOp = OP_GET_LOCAL; getOp = OP_GET_LOCAL;
@ -379,10 +389,24 @@ static void namedVariable(Token name, bool canAssign) {
setOp = OP_SET_GLOBAL; setOp = OP_SET_GLOBAL;
setLongOp = OP_SET_GLOBAL_LONG; 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)) { if (canAssign && match(TOKEN_EQUAL)) {
expression(); expression();
if (indexed) {
if (arg < 256) { 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); emitBytes(setOp, arg);
} else { } else {
emitByte(setLongOp); emitByte(setLongOp);
@ -399,6 +423,9 @@ static void namedVariable(Token name, bool canAssign) {
emitByte(arg & 0xFF00 >> 8); emitByte(arg & 0xFF00 >> 8);
emitByte(arg & 0xFF); emitByte(arg & 0xFF);
} }
if (indexed) {
emitByte(OP_INDEX_FLIPPED);
}
} }
} }
@ -415,7 +442,7 @@ static void and_(bool canAssign) {
patchJump(endJump); patchJump(endJump);
} }
static void array_unary(bool canAssign) { static void array(bool canAssign) {
expression(); expression();
if (check(TOKEN_COMMA)) { if (check(TOKEN_COMMA)) {
int numValues = 0; int numValues = 0;
@ -430,23 +457,13 @@ static void array_unary(bool canAssign) {
emitByte(OP_ARRAY); emitByte(OP_ARRAY);
emitByte(numValues + 1); emitByte(numValues + 1);
} else { } else {
expression();
emitByte(OP_ARRAY); emitByte(OP_ARRAY);
emitByte(1); emitByte(1);
} }
consume(TOKEN_RIGHT_BRACKET, "Unterminated array"); consume(TOKEN_RIGHT_BRACKET, "Unterminated array");
} }
static void compiler_index(bool canAssign) { static void hash(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) {
expression(); expression();
if (check(TOKEN_ROCKET)) { if (check(TOKEN_ROCKET)) {
int numValues = 0; int numValues = 0;
@ -466,6 +483,8 @@ static void hash_unary(bool canAssign) {
emitByte(OP_HASH); emitByte(OP_HASH);
emitByte(numValues + 1); emitByte(numValues + 1);
} else { } else {
consume(TOKEN_ROCKET, "=> must follow every key in a hash");
expression();
emitByte(OP_HASH); emitByte(OP_HASH);
emitByte(1); emitByte(1);
} }
@ -474,9 +493,9 @@ static void hash_unary(bool canAssign) {
ParseRule rules[] = { { grouping, NULL, PREC_CALL }, // TOKEN_LEFT_PAREN ParseRule rules[] = { { grouping, NULL, PREC_CALL }, // TOKEN_LEFT_PAREN
{ NULL, NULL, PREC_NONE }, // TOKEN_RIGHT_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 { 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_RIGHT_BRACKET
{ NULL, NULL, PREC_NONE }, // TOKEN_COMMA { NULL, NULL, PREC_NONE }, // TOKEN_COMMA
{ NULL, NULL, PREC_CALL }, // TOKEN_DOT { NULL, NULL, PREC_CALL }, // TOKEN_DOT

View File

@ -87,6 +87,10 @@ int disassembleInstruction(Chunk* chunk, int offset) {
return simpleInstruction("OP_POP", offset); return simpleInstruction("OP_POP", offset);
case OP_INDEX: case OP_INDEX:
return simpleInstruction("OP_INDEX", offset); 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: case OP_GET_LOCAL:
return byteInstruction("OP_GET_LOCAL", chunk, offset); return byteInstruction("OP_GET_LOCAL", chunk, offset);
case OP_SET_LOCAL: case OP_SET_LOCAL:

106
vm.c
View File

@ -110,6 +110,62 @@ static inline Value readConstantLong() {
return vm.chunk->constants.values[constant]; 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() { static InterpretResult run() {
for (;;) { for (;;) {
#ifdef DEBUG_TRACE_EXECUTION #ifdef DEBUG_TRACE_EXECUTION
@ -184,40 +240,45 @@ static InterpretResult run() {
case OP_INDEX: { case OP_INDEX: {
Value index = pop(); Value index = pop();
Value val = 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)) { if (IS_OBJ(val)) {
Obj* obj = AS_OBJ(val); Obj* obj = AS_OBJ(val);
switch (obj->type) { switch (obj->type) {
case OBJ_ARRAY: { case OBJ_ARRAY: {
ValueArray* valArray = AS_VARRAY(val); ValueArray* valArray = AS_VARRAY(val);
if (!IS_NUMBER(index)) { int i;
runtimeError("Array index must be a non-negative integer."); if (!check_array_index(index, &i)) {
printf("ERR\n");
return INTERPRET_RUNTIME_ERROR; return INTERPRET_RUNTIME_ERROR;
} }
double i_double = AS_NUMBER(index); if (i>=valArray->count) {
if (i_double < 0) { int num_nils=i-valArray->count;
runtimeError("Array index must be a non-negative integer."); for (;num_nils>0;num_nils--) {
return INTERPRET_RUNTIME_ERROR; writeValueArray(valArray,NIL_VAL);
} }
if ((int) i_double != i_double) { writeValueArray(valArray,set_val);
runtimeError("Array index must be a non-negative integer."); } else {
return INTERPRET_RUNTIME_ERROR; valArray->values[i]=set_val;
} }
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; break;
} }
case OBJ_HASH: { case OBJ_HASH: {
Table* hashTable = AS_HASH(val); Table* hashTable = AS_HASH(val);
Value* value = malloc(sizeof(Value)); tableSet(hashTable, index, set_val);
if (tableGet(hashTable, index, value)) {
push(*value);
} else {
push(NIL_VAL);
}
break; break;
} }
default: default:
@ -228,6 +289,7 @@ static InterpretResult run() {
runtimeError("Cannot index value."); runtimeError("Cannot index value.");
return INTERPRET_RUNTIME_ERROR; return INTERPRET_RUNTIME_ERROR;
} }
push(set_val);
break; break;
} }
case OP_GET_LOCAL: { case OP_GET_LOCAL: {
@ -370,7 +432,7 @@ static InterpretResult run() {
break; break;
} }
} }
return INTERPRET_RUNTIME_ERROR; return INTERPRET_OK;
} }
InterpretResult interpret(const char* source, bool repl) { InterpretResult interpret(const char* source, bool repl) {