From 9a752264916f9e4f6510786f2190e7715bab6cd0 Mon Sep 17 00:00:00 2001 From: Kayne Ruse Date: Fri, 24 Apr 2026 11:31:54 +1000 Subject: [PATCH] Implemented the attribute operator, using a period --- source/toy_ast.c | 11 +++++++++++ source/toy_ast.h | 9 +++++++++ source/toy_compiler.c | 20 ++++++++++++++++++++ source/toy_opcodes.h | 1 + source/toy_parser.c | 28 +++++++++++++++++++++++++++- source/toy_vm.c | 17 +++++++++++++++++ tests/units/test_attribute.c | 9 +++++++++ 7 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 tests/units/test_attribute.c diff --git a/source/toy_ast.c b/source/toy_ast.c index 3c823fa..bb7de9d 100644 --- a/source/toy_ast.c +++ b/source/toy_ast.c @@ -234,6 +234,16 @@ void Toy_private_emitAstFunctionInvokation(Toy_Bucket** bucketHandle, Toy_Ast** (*astHandle) = tmp; } +void Toy_private_emitAstAttribute(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* expr) { + Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast)); + + tmp->type = TOY_AST_ATTRIBUTE; + tmp->attribute.left = (*astHandle); + tmp->attribute.right = expr; + + (*astHandle) = tmp; +} + void Toy_private_emitAstStackPop(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) { Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast)); @@ -294,6 +304,7 @@ const char* Toy_private_getAstTypeAsCString(Toy_AstType type) { case TOY_AST_FN_DECLARE: return "FN_DECLARE"; case TOY_AST_FN_INVOKE: return "FN_INVOKE"; + case TOY_AST_ATTRIBUTE: return "ATTRIBUTE"; case TOY_AST_STACK_POP: return "STACK_POP"; diff --git a/source/toy_ast.h b/source/toy_ast.h index f66176a..2a03653 100644 --- a/source/toy_ast.h +++ b/source/toy_ast.h @@ -33,6 +33,7 @@ typedef enum Toy_AstType { TOY_AST_FN_DECLARE, TOY_AST_FN_INVOKE, + TOY_AST_ATTRIBUTE, TOY_AST_STACK_POP, //BUGFIX: force a single stack pop for expression statements @@ -219,6 +220,12 @@ typedef struct Toy_AstFnInvoke { Toy_Ast* args; } Toy_AstFnInvoke; +typedef struct Toy_AstAttribute { + Toy_AstType type; + Toy_Ast* left; + Toy_Ast* right; +} Toy_AstAttribute; + typedef struct Toy_AstStackPop { Toy_AstType type; Toy_Ast* child; @@ -259,6 +266,7 @@ union Toy_Ast { //see 'test_ast.c' for bitness tests Toy_AstVarAccess varAccess; Toy_AstFnDeclare fnDeclare; Toy_AstFnInvoke fnInvoke; + Toy_AstAttribute attribute; Toy_AstStackPop stackPop; Toy_AstPass pass; Toy_AstError error; @@ -291,6 +299,7 @@ void Toy_private_emitAstVariableAccess(Toy_Bucket** bucketHandle, Toy_Ast** astH void Toy_private_emitAstFunctionDeclaration(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_String* name, Toy_Ast* params, Toy_Ast* body); void Toy_private_emitAstFunctionInvokation(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* params); +void Toy_private_emitAstAttribute(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* expr); void Toy_private_emitAstStackPop(Toy_Bucket** bucketHandle, Toy_Ast** astHandle); diff --git a/source/toy_compiler.c b/source/toy_compiler.c index 30ee75f..1ebab0e 100644 --- a/source/toy_compiler.c +++ b/source/toy_compiler.c @@ -1080,6 +1080,22 @@ static unsigned int writeInstructionFnInvoke(Toy_Bytecode** mb, Toy_AstFnInvoke return 0; } +static unsigned int writeInstructionAttribute(Toy_Bytecode** mb, Toy_AstAttribute ast) { + //write the lhs normally + unsigned int result = writeBytecodeFromAst(mb, ast.left); + + //write the attribute's identifier + result += writeBytecodeFromAst(mb, ast.right); + + //exactly what this results in is type-dependant + EMIT_BYTE(mb, code,TOY_OPCODE_ATTRIBUTE); + EMIT_BYTE(mb, code, 0); + EMIT_BYTE(mb, code, 0); + EMIT_BYTE(mb, code, 0); + + return 1; +} + static unsigned int writeInstructionStackPop(Toy_Bytecode** mb, Toy_AstStackPop ast) { unsigned int result = writeBytecodeFromAst(mb, ast.child); @@ -1211,6 +1227,10 @@ static unsigned int writeBytecodeFromAst(Toy_Bytecode** mb, Toy_Ast* ast) { result += writeInstructionFnInvoke(mb, ast->fnInvoke); break; + case TOY_AST_ATTRIBUTE: + result += writeInstructionAttribute(mb, ast->attribute); + break; + case TOY_AST_STACK_POP: result += writeInstructionStackPop(mb, ast->stackPop); break; diff --git a/source/toy_opcodes.h b/source/toy_opcodes.h index 156e1ee..1a97c97 100644 --- a/source/toy_opcodes.h +++ b/source/toy_opcodes.h @@ -11,6 +11,7 @@ typedef enum Toy_OpcodeType { TOY_OPCODE_ASSIGN_COMPOUND, //assign to a compound's internals TOY_OPCODE_ACCESS, TOY_OPCODE_INVOKE, //for calling functions + TOY_OPCODE_ATTRIBUTE, //for accessing parts of compounds TOY_OPCODE_DUPLICATE, //duplicate the top of the stack TOY_OPCODE_ELIMINATE, //remove the top of the stack diff --git a/source/toy_parser.c b/source/toy_parser.c index 91bd85b..d004ebb 100644 --- a/source/toy_parser.c +++ b/source/toy_parser.c @@ -121,6 +121,7 @@ static Toy_AstFlag compound(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_A static Toy_AstFlag aggregate(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle); static Toy_AstFlag unaryPostfix(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle); static Toy_AstFlag invoke(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle); +static Toy_AstFlag attribute(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle); //precedence definitions static ParsingTuple parsingRulesetTable[] = { @@ -212,7 +213,7 @@ static ParsingTuple parsingRulesetTable[] = { {PREC_NONE,NULL,NULL},// TOY_TOKEN_OPERATOR_SEMICOLON, {PREC_GROUP,NULL,aggregate},// TOY_TOKEN_OPERATOR_COMMA, - {PREC_NONE,NULL,NULL},// TOY_TOKEN_OPERATOR_DOT, + {PREC_CALL,NULL,attribute},// TOY_TOKEN_OPERATOR_DOT, {PREC_UNARY,NULL,binary},// TOY_TOKEN_OPERATOR_CONCAT, {PREC_NONE,NULL,NULL},// TOY_TOKEN_OPERATOR_REST, @@ -734,6 +735,31 @@ static Toy_AstFlag invoke(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast return TOY_AST_FLAG_INVOKATION; } +static Toy_AstFlag attribute(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) { + //infix must advance + advance(parser); + + if (parser->previous.type == TOY_TOKEN_OPERATOR_DOT) { + Toy_Ast* expr = NULL; + parsePrecedence(bucketHandle, parser, &expr, PREC_PRIMARY); + + //hijack access node and take the value (presumably an identifier) + if (expr->type != TOY_AST_VAR_ACCESS || expr->varAccess.child == NULL || expr->varAccess.child->type != TOY_AST_VALUE) { + printError(parser, parser->previous, "Malformed expression passed to attribute precedence rule"); + Toy_private_emitAstError(bucketHandle, rootHandle); + return TOY_AST_FLAG_NONE; + } + + Toy_private_emitAstAttribute(bucketHandle, rootHandle, expr->varAccess.child); + return TOY_AST_FLAG_NONE; + } + else { + printError(parser, parser->previous, "Unexpected token passed to attribute precedence rule"); + Toy_private_emitAstError(bucketHandle, rootHandle); + return TOY_AST_FLAG_NONE; + } +} + //grammar rules static void parsePrecedence(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle, ParsingPrecedence precRule) { //'step over' the token to parse diff --git a/source/toy_vm.c b/source/toy_vm.c index 13203e1..c36b21a 100644 --- a/source/toy_vm.c +++ b/source/toy_vm.c @@ -431,6 +431,19 @@ 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); + + //URGENT: type-based attributes + Toy_pushStack(&vm->stack, TOY_VALUE_FROM_NULL()); //tmp + + //cleanup + Toy_freeValue(value); + Toy_freeValue(attribute); +} + static void processDuplicate(Toy_VM* vm) { Toy_Value value = Toy_copyValue(&vm->memoryBucket, Toy_peekStack(&vm->stack)); Toy_pushStack(&vm->stack, value); @@ -958,6 +971,10 @@ static unsigned int process(Toy_VM* vm) { processInvoke(vm); break; + case TOY_OPCODE_ATTRIBUTE: + processAttribute(vm); + break; + case TOY_OPCODE_DUPLICATE: processDuplicate(vm); break; diff --git a/tests/units/test_attribute.c b/tests/units/test_attribute.c new file mode 100644 index 0000000..303b58a --- /dev/null +++ b/tests/units/test_attribute.c @@ -0,0 +1,9 @@ +#include "toy_console_colors.h" + +#include +#include + +int main(void) { + printf(TOY_CC_WARN "Test not yet implemented: %s\n" TOY_CC_RESET, __FILE__); + return 0; +} \ No newline at end of file