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'
This commit is contained in:
2026-04-24 17:31:17 +10:00
parent b589392b9e
commit 88e9794952
10 changed files with 189 additions and 45 deletions
+4
View File
@@ -186,6 +186,10 @@ int inspect_instruction(unsigned char* bytecode, unsigned int pc, unsigned int j
bytecode[pc + 2]); bytecode[pc + 2]);
return 4; 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: case TOY_OPCODE_DUPLICATE:
printf(MARKER "DUPLICATE %s\n", MARKER_VALUE(pc, unsigned char), bytecode[pc + 1] ? "and ACCESS" : ""); printf(MARKER "DUPLICATE %s\n", MARKER_VALUE(pc, unsigned char), bytecode[pc + 1] ? "and ACCESS" : "");
return 4; return 4;
-22
View File
@@ -8,27 +8,11 @@
#include "toy_compiler.h" #include "toy_compiler.h"
#include "toy_vm.h" #include "toy_vm.h"
#include "standard_library.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
//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) { 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 //open the file
FILE* file = fopen(path, "rb"); FILE* file = fopen(path, "rb");
if (file == NULL) { if (file == NULL) {
@@ -340,12 +324,6 @@ int repl(const char* filepath, bool verbose) {
Toy_VM vm; Toy_VM vm;
Toy_initVM(&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 printf("%s> ", prompt); //shows the terminal prompt and begin
//read from the terminal //read from the terminal
+5 -9
View File
@@ -1,10 +1,6 @@
print "hello world"; var array = [1,2,3];
print "hello world";
print "hello world"; array.pushBack(4);
{
print "hello world"; print array.popBack();
{
print "hello world";
}
}
+1
View File
@@ -85,6 +85,7 @@ typedef enum Toy_AstFlag {
TOY_AST_FLAG_POSTFIX_DECREMENT = 44, TOY_AST_FLAG_POSTFIX_DECREMENT = 44,
TOY_AST_FLAG_INVOKATION = 45, TOY_AST_FLAG_INVOKATION = 45,
TOY_AST_FLAG_ATTRIBUTE = 46,
// TOY_AST_FLAG_TERNARY, // TOY_AST_FLAG_TERNARY,
} Toy_AstFlag; } Toy_AstFlag;
+126
View File
@@ -0,0 +1,126 @@
#include "toy_attributes.h"
#include "toy_console_colors.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
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();
}
+22
View File
@@ -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
+1 -1
View File
@@ -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) { static unsigned int writeInstructionAssign(Toy_Bytecode** mb, Toy_AstVarAssign ast, bool chainedAssignment) {
unsigned int result = 0; 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 //target is a variable name
if (ast.target->type == TOY_AST_VALUE && TOY_VALUE_IS_STRING(ast.target->value.value)) { if (ast.target->type == TOY_AST_VALUE && TOY_VALUE_IS_STRING(ast.target->value.value)) {
+1 -1
View File
@@ -751,7 +751,7 @@ static Toy_AstFlag attribute(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_
} }
Toy_private_emitAstAttribute(bucketHandle, rootHandle, expr->varAccess.child); Toy_private_emitAstAttribute(bucketHandle, rootHandle, expr->varAccess.child);
return TOY_AST_FLAG_NONE; return TOY_AST_FLAG_ATTRIBUTE;
} }
else { else {
printError(parser, parser->previous, "Unexpected token passed to attribute precedence rule"); printError(parser, parser->previous, "Unexpected token passed to attribute precedence rule");
+29 -12
View File
@@ -3,6 +3,7 @@
#include "toy_print.h" #include "toy_print.h"
#include "toy_opcodes.h" #include "toy_opcodes.h"
#include "toy_attributes.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@@ -434,22 +435,38 @@ static void processInvoke(Toy_VM* vm) {
static void processAttribute(Toy_VM* vm) { static void processAttribute(Toy_VM* vm) {
//get the compound & attribute //get the compound & attribute
Toy_Value attribute = Toy_popStack(&vm->stack); 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_Value result = TOY_VALUE_FROM_NULL();
Toy_pushStack(&vm->stack, TOY_VALUE_FROM_NULL()); //tmp
//string.length //type-based attributes
//array.length if (TOY_VALUE_IS_STRING(compound)) {
//array.pushFront(x) result = handleStringAttributes(vm, compound, attribute);
//array.pushBack(x) }
//array.popFront() else if (TOY_VALUE_IS_ARRAY(compound)) {
//array.popBack() result = handleArrayAttributes(vm, compound, attribute);
//array.toString() }
//table etc. 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 //cleanup
Toy_freeValue(value);
Toy_freeValue(attribute); Toy_freeValue(attribute);
} }