From 88e97949521b47b6cec7db8fa85a1de1f5979796 Mon Sep 17 00:00:00 2001 From: Kayne Ruse Date: Fri, 24 Apr 2026 17:31:17 +1000 Subject: [PATCH] Implemented the following attributes: * String.length * String.asUpper * String.asLower * array.length * array.pushBack(x) * array.popBack() The remaining attributes are listed in 'toy_attributes.h' --- repl/bytecode_inspector.c | 4 + repl/main.c | 22 --- scripts/blockhead.toy | 14 +- source/toy_ast.h | 1 + source/toy_attributes.c | 126 ++++++++++++++++++ source/toy_attributes.h | 22 +++ source/toy_compiler.c | 2 +- source/toy_parser.c | 2 +- source/toy_vm.c | 41 ++++-- .../{test_attribute.c => test_attributes.c} | 0 10 files changed, 189 insertions(+), 45 deletions(-) create mode 100644 source/toy_attributes.c create mode 100644 source/toy_attributes.h rename tests/units/{test_attribute.c => test_attributes.c} (100%) diff --git a/repl/bytecode_inspector.c b/repl/bytecode_inspector.c index 7a2b95e..a78c538 100644 --- a/repl/bytecode_inspector.c +++ b/repl/bytecode_inspector.c @@ -186,6 +186,10 @@ int inspect_instruction(unsigned char* bytecode, unsigned int pc, unsigned int j bytecode[pc + 2]); return 4; + case TOY_OPCODE_ATTRIBUTE: + printf(MARKER "ATTRIBUTE (accessed from a compound)\n", MARKER_VALUE(pc, unsigned char)); + return 4; + case TOY_OPCODE_DUPLICATE: printf(MARKER "DUPLICATE %s\n", MARKER_VALUE(pc, unsigned char), bytecode[pc + 1] ? "and ACCESS" : ""); return 4; diff --git a/repl/main.c b/repl/main.c index 6ac2c9f..988c39c 100644 --- a/repl/main.c +++ b/repl/main.c @@ -8,27 +8,11 @@ #include "toy_compiler.h" #include "toy_vm.h" -#include "standard_library.h" - #include #include #include -//utilities -#if defined(_WIN32) || defined(_WIN64) - #define FLIPSLASH(str) for (int i = 0; str != NULL && str[i]; i++) str[i] = str[i] == '/' ? '\\' : str[i]; -#else - #define FLIPSLASH(str) for (int i = 0; str != NULL && str[i]; i++) str[i] = str[i] == '\\' ? '/' : str[i]; -#endif - unsigned char* readFile(char* path, int* size) { - //BUGFIX: fix the path based on platform - it might be slower, but it's better than dealing with platform crap - int pathLength = strlen(path); - char realPath[pathLength + 1]; - strncpy(realPath, path, pathLength); - realPath[pathLength] = '\0'; - FLIPSLASH(realPath); - //open the file FILE* file = fopen(path, "rb"); if (file == NULL) { @@ -340,12 +324,6 @@ int repl(const char* filepath, bool verbose) { Toy_VM vm; Toy_initVM(&vm); - //hacky test - if (vm.scope == NULL) { - vm.scope = Toy_pushScope(&vm.memoryBucket, NULL); - initStandardLibrary(&vm); - } - printf("%s> ", prompt); //shows the terminal prompt and begin //read from the terminal diff --git a/scripts/blockhead.toy b/scripts/blockhead.toy index 4b30a5f..df8b3d0 100644 --- a/scripts/blockhead.toy +++ b/scripts/blockhead.toy @@ -1,10 +1,6 @@ -print "hello world"; -print "hello world"; -print "hello world"; -{ - print "hello world"; - { - print "hello world"; - } -} \ No newline at end of file +var array = [1,2,3]; + +array.pushBack(4); + +print array.popBack(); \ No newline at end of file diff --git a/source/toy_ast.h b/source/toy_ast.h index 2a03653..4c18bb9 100644 --- a/source/toy_ast.h +++ b/source/toy_ast.h @@ -85,6 +85,7 @@ typedef enum Toy_AstFlag { TOY_AST_FLAG_POSTFIX_DECREMENT = 44, TOY_AST_FLAG_INVOKATION = 45, + TOY_AST_FLAG_ATTRIBUTE = 46, // TOY_AST_FLAG_TERNARY, } Toy_AstFlag; diff --git a/source/toy_attributes.c b/source/toy_attributes.c new file mode 100644 index 0000000..e30baa9 --- /dev/null +++ b/source/toy_attributes.c @@ -0,0 +1,126 @@ +#include "toy_attributes.h" +#include "toy_console_colors.h" + +#include +#include +#include +#include + +Toy_Value handleStringAttributes(Toy_VM* vm, Toy_Value compound, Toy_Value attribute) { + if (strncmp(TOY_VALUE_AS_STRING(attribute)->leaf.data, "length", 6) == 0) { + return TOY_VALUE_FROM_INTEGER(TOY_VALUE_AS_STRING(compound)->info.length); + } + else if (strncmp(TOY_VALUE_AS_STRING(attribute)->leaf.data, "asUpper", 7) == 0) { + char* buffer = Toy_getStringRaw(TOY_VALUE_AS_STRING(compound)); + for (int i = 0; buffer[i] != '\0'; i++) { + buffer[i] = toupper(buffer[i]); + } + Toy_String* str = Toy_createStringLength(&vm->memoryBucket, buffer, strlen(buffer)); + free(buffer); + return TOY_VALUE_FROM_STRING(str); + } + else if (strncmp(TOY_VALUE_AS_STRING(attribute)->leaf.data, "asLower", 7) == 0) { + char* buffer = Toy_getStringRaw(TOY_VALUE_AS_STRING(compound)); + for (int i = 0; buffer[i] != '\0'; i++) { + buffer[i] = tolower(buffer[i]); + } + Toy_String* str = Toy_createStringLength(&vm->memoryBucket, buffer, strlen(buffer)); + free(buffer); + return TOY_VALUE_FROM_STRING(str); + } + else { + char buffer[256]; + snprintf(buffer, 256, "Unknown attribute '%s' of type '%s'", TOY_VALUE_AS_STRING(attribute)->leaf.data, Toy_private_getValueTypeAsCString(compound.type)); + Toy_error(buffer); + return TOY_VALUE_FROM_NULL(); + } +} + +static void attr_arrayPushBack(Toy_VM* vm) { + Toy_Value compound = Toy_popStack(&vm->stack); + Toy_Value element = Toy_popStack(&vm->stack); + + Toy_Array* array = TOY_VALUE_AS_ARRAY(compound); + + //check the capacity limit + if (array->count == array->capacity) { + //correct the source value's pointer + array = Toy_resizeArray(array, array->capacity * TOY_ARRAY_EXPANSION_RATE); + if (TOY_VALUE_IS_REFERENCE(compound) && compound.as.reference->type == TOY_VALUE_ARRAY) { + compound.as.reference->as.array = array; + } + else { + char buffer[256]; + snprintf(buffer, 256, "Unknown error at %s %d", __FILE__, __LINE__); + Toy_error(buffer); + } + } + + array->data[array->count] = element; + array->count++; +} + +static void attr_arrayPopBack(Toy_VM* vm) { + Toy_Value compound = Toy_popStack(&vm->stack); + + Toy_Array* array = TOY_VALUE_AS_ARRAY(compound); + + //empty returns nothing + if (array->count == 0) { + Toy_pushStack(&vm->stack, TOY_VALUE_FROM_NULL()); + return; + } + + Toy_Value element = array->data[array->count-1]; + array->count--; + + Toy_pushStack(&vm->stack, element); +} + +static void attr_arrayForEach(Toy_VM* vm) { + (void)vm; + //URGENT: attr_arrayForEach +} + +static void attr_arraySort(Toy_VM* vm) { + (void)vm; + //URGENT: attr_arraySort +} + +Toy_Value handleArrayAttributes(Toy_VM* vm, Toy_Value compound, Toy_Value attribute) { + if (strncmp(TOY_VALUE_AS_STRING(attribute)->leaf.data, "length", 6) == 0) { + return TOY_VALUE_FROM_INTEGER(TOY_VALUE_AS_ARRAY(compound)->count); + } + else if (strncmp(TOY_VALUE_AS_STRING(attribute)->leaf.data, "pushBack", 8) == 0) { + Toy_Function* fn = Toy_createFunctionFromCallback(&vm->memoryBucket, attr_arrayPushBack); + return TOY_VALUE_FROM_FUNCTION(fn); + } + else if (strncmp(TOY_VALUE_AS_STRING(attribute)->leaf.data, "popBack", 7) == 0) { + Toy_Function* fn = Toy_createFunctionFromCallback(&vm->memoryBucket, attr_arrayPopBack); + return TOY_VALUE_FROM_FUNCTION(fn); + } + else if (strncmp(TOY_VALUE_AS_STRING(attribute)->leaf.data, "forEach", 7) == 0) { + Toy_Function* fn = Toy_createFunctionFromCallback(&vm->memoryBucket, attr_arrayForEach); + return TOY_VALUE_FROM_FUNCTION(fn); + } + else if (strncmp(TOY_VALUE_AS_STRING(attribute)->leaf.data, "sort", 4) == 0) { + Toy_Function* fn = Toy_createFunctionFromCallback(&vm->memoryBucket, attr_arraySort); + return TOY_VALUE_FROM_FUNCTION(fn); + } + else { + char buffer[256]; + snprintf(buffer, 256, "Unknown attribute '%s' of type '%s'", TOY_VALUE_AS_STRING(attribute)->leaf.data, Toy_private_getValueTypeAsCString(compound.type)); + Toy_error(buffer); + return TOY_VALUE_FROM_NULL(); + } + + return TOY_VALUE_FROM_NULL(); +} + +Toy_Value handleTableAttributes(Toy_VM* vm, Toy_Value compound, Toy_Value attribute) { + //URGENT: handleTableAttributes + (void)vm; + (void)compound; + (void)attribute; + return TOY_VALUE_FROM_NULL(); +} \ No newline at end of file diff --git a/source/toy_attributes.h b/source/toy_attributes.h new file mode 100644 index 0000000..cb8e93a --- /dev/null +++ b/source/toy_attributes.h @@ -0,0 +1,22 @@ +#pragma once + +#include "toy_value.h" +#include "toy_vm.h" + +Toy_Value handleStringAttributes(Toy_VM* vm, Toy_Value compound, Toy_Value attribute); +Toy_Value handleArrayAttributes(Toy_VM* vm, Toy_Value compound, Toy_Value attribute); +Toy_Value handleTableAttributes(Toy_VM* vm, Toy_Value compound, Toy_Value attribute); + +// string.length +// string.asUpper +// string.asLower +// array.length +// array.pushBack(x) +// array.popBack() +// array.forEach(fn) // fn(x) -> void +// array.sort(fn) // fn(a,b) -> int +// table.length +// table.insert(x) +// table.hasKey(x) +// table.remove(x) +// table.forEach(fn) // fn(x) -> void diff --git a/source/toy_compiler.c b/source/toy_compiler.c index 684a1de..9a7e7f9 100644 --- a/source/toy_compiler.c +++ b/source/toy_compiler.c @@ -800,7 +800,7 @@ static unsigned int writeInstructionVarDeclare(Toy_Bytecode** mb, Toy_AstVarDecl static unsigned int writeInstructionAssign(Toy_Bytecode** mb, Toy_AstVarAssign ast, bool chainedAssignment) { unsigned int result = 0; - //URGENT: flip the order of target & value, to allow chained assignment AND multiple return values + //BUG: flip the order of target & value, to allow chained assignment AND multiple return values //target is a variable name if (ast.target->type == TOY_AST_VALUE && TOY_VALUE_IS_STRING(ast.target->value.value)) { diff --git a/source/toy_parser.c b/source/toy_parser.c index d004ebb..f28e90b 100644 --- a/source/toy_parser.c +++ b/source/toy_parser.c @@ -751,7 +751,7 @@ static Toy_AstFlag attribute(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_ } Toy_private_emitAstAttribute(bucketHandle, rootHandle, expr->varAccess.child); - return TOY_AST_FLAG_NONE; + return TOY_AST_FLAG_ATTRIBUTE; } else { printError(parser, parser->previous, "Unexpected token passed to attribute precedence rule"); diff --git a/source/toy_vm.c b/source/toy_vm.c index d8a7c87..df0c3fa 100644 --- a/source/toy_vm.c +++ b/source/toy_vm.c @@ -3,6 +3,7 @@ #include "toy_print.h" #include "toy_opcodes.h" +#include "toy_attributes.h" #include #include @@ -434,22 +435,38 @@ static void processInvoke(Toy_VM* vm) { static void processAttribute(Toy_VM* vm) { //get the compound & attribute Toy_Value attribute = Toy_popStack(&vm->stack); - Toy_Value value = Toy_popStack(&vm->stack); + Toy_Value compound = Toy_popStack(&vm->stack); - //URGENT: type-based attributes - Toy_pushStack(&vm->stack, TOY_VALUE_FROM_NULL()); //tmp + Toy_Value result = TOY_VALUE_FROM_NULL(); - //string.length - //array.length - //array.pushFront(x) - //array.pushBack(x) - //array.popFront() - //array.popBack() - //array.toString() - //table etc. + //type-based attributes + if (TOY_VALUE_IS_STRING(compound)) { + result = handleStringAttributes(vm, compound, attribute); + } + else if (TOY_VALUE_IS_ARRAY(compound)) { + result = handleArrayAttributes(vm, compound, attribute); + } + else if (TOY_VALUE_IS_TABLE(compound)) { + result = handleTableAttributes(vm, compound, attribute); + } + else { + char buffer[256]; + snprintf(buffer, 256, "Can't access an attribute of type '%s'", Toy_private_getValueTypeAsCString(compound.type)); + Toy_error(buffer); + Toy_pushStack(&vm->stack, TOY_VALUE_FROM_NULL()); + return; + } + + //BUGFIX: check for callable native functions, so they can access the compound too + if (TOY_VALUE_IS_FUNCTION(result) && TOY_VALUE_AS_FUNCTION(result)->type == TOY_FUNCTION_NATIVE) { + //WONTFIX: `print array.pushBack;' will leave the compound's reference on the stack + Toy_pushStack(&vm->stack, compound); + } + + //leave the result on the stack + Toy_pushStack(&vm->stack, result); //cleanup - Toy_freeValue(value); Toy_freeValue(attribute); } diff --git a/tests/units/test_attribute.c b/tests/units/test_attributes.c similarity index 100% rename from tests/units/test_attribute.c rename to tests/units/test_attributes.c