From 61a105db2d018e4c12875dd57cda8017bd8db2e3 Mon Sep 17 00:00:00 2001 From: Kayne Ruse Date: Sat, 7 Dec 2024 15:35:06 +1100 Subject: [PATCH] Compound assignment for arrays is working, untested, read more I added reference values in 62ca7a1fb7d139948a8f6fb338630f960abebef6, 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. --- scripts/compound.toy | 19 ++++++++++++++ source/toy_array.h | 3 ++- source/toy_opcodes.h | 5 +++- source/toy_parser.c | 4 +-- source/toy_routine.c | 25 +++++++++++++----- source/toy_scope.c | 10 +++----- source/toy_vm.c | 61 +++++++++++++++++++++++++++++++++++++++++--- 7 files changed, 108 insertions(+), 19 deletions(-) create mode 100644 scripts/compound.toy diff --git a/scripts/compound.toy b/scripts/compound.toy new file mode 100644 index 0000000..e70206d --- /dev/null +++ b/scripts/compound.toy @@ -0,0 +1,19 @@ + + +//1-D array +var arr = [1, 2, 3]; +arr[1] = 6; +print arr; + + + +//we need to go deeper +var barr = [ + [1, 2, 3], + [4, 5, 6], + [7, 8, 9] +]; + +barr[1][1] = 99; + +print barr; \ No newline at end of file diff --git a/source/toy_array.h b/source/toy_array.h index ec25b5f..f93575a 100644 --- a/source/toy_array.h +++ b/source/toy_array.h @@ -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 \ No newline at end of file diff --git a/source/toy_opcodes.h b/source/toy_opcodes.h index 50cdaac..3878723 100644 --- a/source/toy_opcodes.h +++ b/source/toy_opcodes.h @@ -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 diff --git a/source/toy_parser.c b/source/toy_parser.c index b87883a..a52d7a3 100644 --- a/source/toy_parser.c +++ b/source/toy_parser.c @@ -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; diff --git a/source/toy_routine.c b/source/toy_routine.c index 77577dd..af6090a 100644 --- a/source/toy_routine.c +++ b/source/toy_routine.c @@ -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; } diff --git a/source/toy_scope.c b/source/toy_scope.c index e475861..058b3fb 100644 --- a/source/toy_scope.c +++ b/source/toy_scope.c @@ -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; } diff --git a/source/toy_vm.c b/source/toy_vm.c index fac11db..78cb24a 100644 --- a/source/toy_vm.c +++ b/source/toy_vm.c @@ -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: