Compound assignment for arrays is working, untested, read more

I added reference values in 62ca7a1fb7,
but forgot to mention it. I'm now using references to assign to the
internals of an array, no matter how many levels deep it is.
This commit is contained in:
2024-12-07 15:35:06 +11:00
parent 2324150fd5
commit 61a105db2d
7 changed files with 108 additions and 19 deletions

View File

@@ -41,4 +41,5 @@ TOY_API Toy_Array* Toy_resizeArray(Toy_Array* array, unsigned int capacity);
#define TOY_ARRAY_PUSHBACK(array, value) (TOY_ARRAY_EXPAND(array),(array)->data[(array)->count++] = (value))
#endif
//URGENT: check array length from scripts
//URGENT: get array length in scripts (dot operator?)
//URGENT: array as a type

View File

@@ -1,12 +1,15 @@
#pragma once
typedef enum Toy_OpcodeType {
//This offsets the opcode values, so I can see TOY_OPCODE_READ in GDB clearly
TOY_OPCODE_UNUSED = 0,
//variable instructions
TOY_OPCODE_READ,
TOY_OPCODE_DECLARE,
TOY_OPCODE_ASSIGN,
TOY_OPCODE_ASSIGN_COMPOUND, //assign to a compound's internals
TOY_OPCODE_ACCESS,
TOY_OPCODE_DUPLICATE, //duplicate the top of the stack
//arithmetic instructions

View File

@@ -567,8 +567,7 @@ static Toy_AstFlag group(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast*
}
static Toy_AstFlag compound(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) {
//read in an array or dictionary aggregate
//read in an array or dictionary compound definition
if (parser->previous.type == TOY_TOKEN_OPERATOR_BRACKET_LEFT) {
parsePrecedence(bucketHandle, parser, rootHandle, PREC_GROUP);
consume(parser, TOY_TOKEN_OPERATOR_BRACKET_RIGHT, "Expected ']' at the end of compound expression");
@@ -589,6 +588,7 @@ static Toy_AstFlag aggregate(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_
//infix must advance
advance(parser);
//aggregates are a collection of parts, expressing a larger idea
if (parser->previous.type == TOY_TOKEN_OPERATOR_COMMA) {
parsePrecedence(bucketHandle, parser, rootHandle, PREC_GROUP); //NOT +1, as compounds are right-recursive
return TOY_AST_FLAG_COLLECTION;

View File

@@ -453,8 +453,6 @@ static unsigned int writeInstructionVarDeclare(Toy_Routine** rt, Toy_AstVarDecla
static unsigned int writeInstructionAssign(Toy_Routine** rt, Toy_AstVarAssign ast) {
unsigned int result = 0;
//URGENT: check for LHS in index
//don't treat these as valid values
switch (ast.expr->type) {
case TOY_AST_BLOCK:
@@ -463,7 +461,7 @@ static unsigned int writeInstructionAssign(Toy_Routine** rt, Toy_AstVarAssign as
case TOY_AST_PRINT:
case TOY_AST_VAR_DECLARE:
//emit a compiler error, set the panic flag and skip out
fprintf(stderr, TOY_CC_ERROR "COMPILER ERROR: Invalid AST type found: Malformed assignment\n" TOY_CC_RESET);
fprintf(stderr, TOY_CC_ERROR "COMPILER ERROR: Invalid AST type found: Malformed assignment value\n" TOY_CC_RESET);
(*rt)->panic = true;
return 0;
@@ -471,7 +469,7 @@ static unsigned int writeInstructionAssign(Toy_Routine** rt, Toy_AstVarAssign as
break;
}
//target, based on type
//target is a name string
if (ast.target->type == TOY_AST_VALUE && TOY_VALUE_IS_STRING(ast.target->value.value) && TOY_VALUE_AS_STRING(ast.target->value.value)->type == TOY_STRING_NAME) {
//name string
Toy_String* target = TOY_VALUE_AS_STRING(ast.target->value.value);
@@ -484,9 +482,24 @@ static unsigned int writeInstructionAssign(Toy_Routine** rt, Toy_AstVarAssign as
emitString(rt, target);
}
//target is an indexing of some compound value
else if (ast.target->type == TOY_AST_AGGREGATE && ast.target->aggregate.flag == TOY_AST_FLAG_INDEX) {
writeRoutineCode(rt, ast.target->aggregate.left); //any deeper indexing will just work, using reference values
writeRoutineCode(rt, ast.target->aggregate.right); //key
writeRoutineCode(rt, ast.expr); //value
EMIT_BYTE(rt, code, TOY_OPCODE_ASSIGN_COMPOUND); //uses the top three values on the stack
EMIT_BYTE(rt, code,0);
EMIT_BYTE(rt, code,0);
EMIT_BYTE(rt, code,0);
return 0;
}
else {
//URGENT: assigning to an array member
fprintf(stderr, TOY_CC_ERROR "COMPILER ERROR: TODO at %s %d\n" TOY_CC_RESET, __FILE__, __LINE__);
//unknown target
fprintf(stderr, TOY_CC_ERROR "COMPILER ERROR: Invalid AST type found: Malformed assignment target\n" TOY_CC_RESET);
(*rt)->panic = true;
return 0;
}

View File

@@ -7,8 +7,6 @@
#include "toy_print.h"
//URGENT: don't let references get saved into a scope
//utils
static void incrementRefCount(Toy_Scope* scope) {
for (Toy_Scope* iter = scope; iter; iter = iter->next) {
@@ -110,9 +108,9 @@ void Toy_declareScope(Toy_Scope* scope, Toy_String* key, Toy_Value value) {
//type check
Toy_ValueType kt = Toy_getNameStringType(key);
if (kt != TOY_VALUE_ANY && value.type != TOY_VALUE_NULL && kt != value.type) {
if (kt != TOY_VALUE_ANY && value.type != TOY_VALUE_NULL && kt != value.type && value.type != TOY_VALUE_REFERENCE) {
char buffer[key->length + 256];
sprintf(buffer, "Incorrect value type assigned to in variable declaration '%s' (expected %d, got %d)", key->as.name.data, (int)kt, (int)value.type);
sprintf(buffer, "Incorrect value type in declaration of '%s' (expected %s, got %s)", key->as.name.data, Toy_private_getValueTypeAsCString(kt), Toy_private_getValueTypeAsCString(value.type));
Toy_error(buffer);
return;
}
@@ -147,9 +145,9 @@ void Toy_assignScope(Toy_Scope* scope, Toy_String* key, Toy_Value value) {
//type check
Toy_ValueType kt = Toy_getNameStringType( TOY_VALUE_AS_STRING(entryPtr->key) );
if (kt != TOY_VALUE_ANY && value.type != TOY_VALUE_NULL && kt != value.type) {
if (kt != TOY_VALUE_ANY && value.type != TOY_VALUE_NULL && kt != value.type && value.type != TOY_VALUE_REFERENCE) {
char buffer[key->length + 256];
sprintf(buffer, "Incorrect value type assigned to in variable assignment '%s' (expected %d, got %d)", key->as.name.data, (int)kt, (int)value.type);
sprintf(buffer, "Incorrect value type in assignment of '%s' (expected %s, got %s)", key->as.name.data, Toy_private_getValueTypeAsCString(kt), Toy_private_getValueTypeAsCString(value.type));
Toy_error(buffer);
return;
}

View File

@@ -195,18 +195,68 @@ static void processAssign(Toy_VM* vm) {
//check name string type
if (!TOY_VALUE_IS_STRING(name) || TOY_VALUE_AS_STRING(name)->type != TOY_STRING_NAME) {
Toy_error("Invalid assignment target");
Toy_freeValue(name);
Toy_freeValue(value);
return;
}
//assign it
Toy_assignScope(vm->scope, TOY_VALUE_AS_STRING(name), value);
//URGENT: complex assignments
Toy_assignScope(vm->scope, TOY_VALUE_AS_STRING(name), value); //scope now owns value, doesn't need to be freed
//cleanup
Toy_freeValue(name);
}
static void processAssignCompound(Toy_VM* vm) {
//get the value, key, target
Toy_Value value = Toy_popStack(&vm->stack);
Toy_Value key = Toy_popStack(&vm->stack);
Toy_Value target = Toy_popStack(&vm->stack);
//shake out variable names
if (TOY_VALUE_IS_STRING(target) && TOY_VALUE_AS_STRING(target)->type == TOY_STRING_NAME) {
Toy_Value* valuePtr = Toy_accessScopeAsPointer(vm->scope, TOY_VALUE_AS_STRING(target));
Toy_freeValue(target);
target = TOY_REFERENCE_FROM_POINTER(valuePtr);
}
//assign based on target's type
if (TOY_VALUE_IS_ARRAY(target)) {
if (TOY_VALUE_IS_INTEGER(key) != true) {
Toy_error("Bad key type for assignment target");
Toy_freeValue(target);
Toy_freeValue(key);
Toy_freeValue(value);
return;
}
Toy_Array* array = TOY_VALUE_AS_ARRAY(target);
int index = TOY_VALUE_AS_INTEGER(key);
//bounds check
if (index < 0 || index >= array->count) {
Toy_error("Index of assignment target out of bounds");
Toy_freeValue(target);
Toy_freeValue(key);
Toy_freeValue(value);
}
//set the value
array->data[index] = Toy_copyValue(Toy_unwrapValue(value));
//cleanup
Toy_freeValue(value);
}
else {
Toy_error("Invalid assignment target");
Toy_freeValue(target);
Toy_freeValue(key);
Toy_freeValue(value);
return;
}
}
static void processAccess(Toy_VM* vm) {
Toy_Value name = Toy_popStack(&vm->stack);
@@ -760,6 +810,10 @@ static void process(Toy_VM* vm) {
processAssign(vm);
break;
case TOY_OPCODE_ASSIGN_COMPOUND:
processAssignCompound(vm);
break;
case TOY_OPCODE_ACCESS:
processAccess(vm);
break;
@@ -828,6 +882,7 @@ static void process(Toy_VM* vm) {
processIndex(vm);
break;
case TOY_OPCODE_UNUSED:
case TOY_OPCODE_PASS:
case TOY_OPCODE_ERROR:
case TOY_OPCODE_EOF: