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 {
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,

View File

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

View File

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

108
vm.c
View File

@ -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) {