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 {
|
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,
|
||||||
|
49
compiler.c
49
compiler.c
@ -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
|
||||||
|
4
debug.c
4
debug.c
@ -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
106
vm.c
@ -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) {
|
||||||
|
Loading…
Reference in New Issue
Block a user