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:
parent
701b9aad40
commit
c5b12fe8c9
2
chunk.h
2
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,
|
||||
|
49
compiler.c
49
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 (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
|
||||
|
4
debug.c
4
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:
|
||||
|
106
vm.c
106
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);
|
||||
}
|
||||
if ((int) i_double != i_double) {
|
||||
runtimeError("Array index must be a non-negative integer.");
|
||||
return INTERPRET_RUNTIME_ERROR;
|
||||
writeValueArray(valArray,set_val);
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
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) {
|
||||
|
Loading…
Reference in New Issue
Block a user