From 2a82b0052d3b80b3554685a2961110487464459a Mon Sep 17 00:00:00 2001 From: Kayne Ruse Date: Sat, 23 May 2026 14:40:32 +1000 Subject: [PATCH] TMP: Brain broke, I need a break, don't use this commit --- .notes/for-loop.txt | 23 +++++++++++++ scripts/hello_world.toy | 13 +++----- source/toy_compiler.c | 67 +++++++++++++++++++++++++++++++++++--- source/toy_opcodes.h | 3 +- source/toy_parser.c | 57 ++++++++++++++++++++++++++++---- source/toy_vm.c | 72 ++++++++++++++++++++++++++++++++++++++--- 6 files changed, 209 insertions(+), 26 deletions(-) create mode 100644 .notes/for-loop.txt diff --git a/.notes/for-loop.txt b/.notes/for-loop.txt new file mode 100644 index 0000000..fb57c20 --- /dev/null +++ b/.notes/for-loop.txt @@ -0,0 +1,23 @@ + + +```toy +for (var name in array) { + // +} +``` + + +pushStack name +pushStack array +pushStack counter //tracks the index for each loop + +OPCODE iterable: + pushScope + declare name = array.next() if next is not null else jump to end + + (user code) + + popScope + jump to beginning + +popStack counter, array, name \ No newline at end of file diff --git a/scripts/hello_world.toy b/scripts/hello_world.toy index d572290..a763d0b 100644 --- a/scripts/hello_world.toy +++ b/scripts/hello_world.toy @@ -1,12 +1,7 @@ +var array = ["foo", "bar"]; -fn a(x) { - print x; -} - -fn b() { - return 42; -} - -a(b(), b()); \ No newline at end of file +for (var i in array) { + print i; +} \ No newline at end of file diff --git a/source/toy_compiler.c b/source/toy_compiler.c index 66e67a3..4a96dae 100644 --- a/source/toy_compiler.c +++ b/source/toy_compiler.c @@ -226,6 +226,7 @@ static unsigned int writeBytecodeFromAst(Toy_Bytecode** mb, Toy_Ast* ast); //for static void writeBytecodeBody(Toy_Bytecode* mb, Toy_Ast* ast); static unsigned char* collateBytecodeBody(Toy_Bytecode* mb); static unsigned int writeInstructionAssign(Toy_Bytecode** mb, Toy_AstVarAssign ast, bool chainedAssignment); //forward declare for chaining of var declarations +static unsigned int writeInstructionAccess(Toy_Bytecode** mb, Toy_AstVarAccess ast); static unsigned int writeInstructionFnInvoke(Toy_Bytecode** mb, Toy_AstFnInvoke ast, bool chainedInvoke); static unsigned int writeInstructionValue(Toy_Bytecode** mb, Toy_AstValue ast) { @@ -456,7 +457,7 @@ static unsigned int writeInstructionBinaryShortCircuit(Toy_Bytecode** mb, Toy_As //if the lhs value isn't needed, pop it EMIT_BYTE(mb, code,TOY_OPCODE_ELIMINATE); - EMIT_BYTE(mb, code, 0); + EMIT_BYTE(mb, code, 1); EMIT_BYTE(mb, code, 0); EMIT_BYTE(mb, code, 0); @@ -719,9 +720,65 @@ static unsigned int writeInstructionWhileThen(Toy_Bytecode** mb, Toy_AstWhileThe } static unsigned int writeInstructionForThen(Toy_Bytecode** mb, Toy_AstForThen ast) { - //URGENT: WIP - (void)mb; - (void)ast; + //check the operands + if (ast.condBranch->type != TOY_AST_ITERABLE || ast.condBranch->iterable.left->type != TOY_AST_VAR_DECLARE || ast.condBranch->iterable.right->type != TOY_AST_VAR_ACCESS) { + fprintf(stderr, TOY_CC_ERROR "COMPILER ERROR: Invalid conditional found in a 'for' loop\n" TOY_CC_RESET); + (*mb)->panic = true; + return 0; + } + + //set up the iterable, and the counter + writeInstructionAccess(mb, ast.condBranch->iterable.right->varAccess); + + EMIT_BYTE(mb, code, TOY_OPCODE_READ); + EMIT_BYTE(mb, code, TOY_VALUE_INTEGER); + EMIT_BYTE(mb, code, 0); + EMIT_BYTE(mb, code, 0); + + EMIT_INT(mb, code, 0); //start from zero + + //access [-1] from [-2], incrementing [-1] afterwards + //then if null, execute relative jump + EMIT_BYTE(mb, code, TOY_OPCODE_ITERABLE); + EMIT_BYTE(mb, code, TOY_OP_PARAM_JUMP_RELATIVE); + EMIT_BYTE(mb, code, TOY_OP_PARAM_JUMP_IF_NULL); + EMIT_BYTE(mb, code, 0); + + unsigned int thenParamAddr = SKIP_INT(mb, code); //parameter to be written later + + //push scope (built-into the keyword) + EMIT_BYTE(mb, code, TOY_OPCODE_SCOPE_PUSH); + EMIT_BYTE(mb, code, 0); + EMIT_BYTE(mb, code, 0); + EMIT_BYTE(mb, code, 0); + (*mb)->currentScopeDepth++; + + //delcare the iterator with the given string + EMIT_BYTE(mb, code, TOY_OPCODE_DECLARE); + EMIT_BYTE(mb, code, ast.condBranch->iterable.left->varDeclare.valueType); + EMIT_BYTE(mb, code, ast.condBranch->iterable.left->varDeclare.name->info.length); //quick optimisation to skip a 'strlen()' call + EMIT_BYTE(mb, code, ast.condBranch->iterable.left->varDeclare.constant); //check for constness + + emitString(mb, ast.condBranch->iterable.left->varDeclare.name); + + //write the body + writeBytecodeFromAst(mb, ast.thenBranch); + + //pop scope after each iteration + EMIT_BYTE(mb, code, TOY_OPCODE_SCOPE_POP); + EMIT_BYTE(mb, code, 0); + EMIT_BYTE(mb, code, 0); + EMIT_BYTE(mb, code, 0); + (*mb)->currentScopeDepth--; + + //end of the loop, overwrite the parameter + OVERWRITE_INT(mb, code, thenParamAddr, CURRENT_ADDRESS(mb, code) - (thenParamAddr + 4)); + + //pop the conditional stuff from the stack + EMIT_BYTE(mb, code,TOY_OPCODE_ELIMINATE); + EMIT_BYTE(mb, code, 2); + EMIT_BYTE(mb, code, 0); + EMIT_BYTE(mb, code, 0); (*mb)->panic = true; return 1; @@ -1163,7 +1220,7 @@ static unsigned int writeInstructionStackPop(Toy_Bytecode** mb, Toy_AstStackPop //dead simple EMIT_BYTE(mb, code,TOY_OPCODE_ELIMINATE); - EMIT_BYTE(mb, code, 0); + EMIT_BYTE(mb, code, 1); EMIT_BYTE(mb, code, 0); EMIT_BYTE(mb, code, 0); diff --git a/source/toy_opcodes.h b/source/toy_opcodes.h index 8aedbd4..80b7032 100644 --- a/source/toy_opcodes.h +++ b/source/toy_opcodes.h @@ -12,9 +12,9 @@ typedef enum Toy_OpcodeType { TOY_OPCODE_ACCESS, TOY_OPCODE_INVOKE, //for calling functions TOY_OPCODE_ATTRIBUTE, //for accessing parts of compounds - TOY_OPCODE_ITERABLE, //for operating on all members of a compound TOY_OPCODE_DUPLICATE, //duplicate the top of the stack TOY_OPCODE_ELIMINATE, //remove the top of the stack + TOY_OPCODE_ITERABLE, //for operating on all members of a compound //arithmetic instructions TOY_OPCODE_ADD, @@ -67,5 +67,6 @@ typedef enum Toy_OpParamJumpConditional { TOY_OP_PARAM_JUMP_ALWAYS = 0, TOY_OP_PARAM_JUMP_IF_TRUE = 1, TOY_OP_PARAM_JUMP_IF_FALSE = 2, + TOY_OP_PARAM_JUMP_IF_NULL = 3, } Toy_OpParamJumpConditional; diff --git a/source/toy_parser.c b/source/toy_parser.c index 9b8dcb8..474d2c6 100644 --- a/source/toy_parser.c +++ b/source/toy_parser.c @@ -765,11 +765,17 @@ static Toy_AstFlag iterable(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_A //infix must advance advance(parser); - Toy_Ast* expr = NULL; - parsePrecedence(bucketHandle, parser, &expr, PREC_CALL); - Toy_private_emitAstIterable(bucketHandle, rootHandle, expr); + //check for the correct operands + if ((*rootHandle)->type != TOY_AST_VAR_DECLARE) { + printError(parser, parser->previous, "Expected iterator name not found"); + Toy_private_emitAstError(bucketHandle, rootHandle); + return TOY_AST_FLAG_NONE; + } - //TODO: check for var declare without assignment + Toy_Ast* expr = NULL; + parsePrecedence(bucketHandle, parser, rootHandle, PREC_CALL); + + Toy_private_emitAstIterable(bucketHandle, rootHandle, expr); return TOY_AST_FLAG_NONE; } @@ -785,7 +791,7 @@ static void parsePrecedence(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_A if (prefix == NULL) { //make a nice error message if (Toy_private_findKeywordByType(parser->previous.type)) { - printError(parser, parser->previous, "Found reserved keyword instead"); + printError(parser, parser->previous, "Expected expression, found reserved keyword instead"); } else { printError(parser, parser->previous, "Expected expression"); @@ -909,12 +915,49 @@ static void makeForStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** Toy_Ast* condBranch = NULL; Toy_Ast* thenBranch = NULL; + //BUG: wtf am I doing here? + //for (condBranch) consume(parser, TOY_TOKEN_OPERATOR_PAREN_LEFT, "Expected '(' after 'for' keyword"); + consume(parser, TOY_TOKEN_KEYWORD_VAR, "Expected 'var' in 'for' conditional"); + + //WARN: duped from var declare + consume(parser, TOY_TOKEN_NAME, "Expected variable name after 'var' keyword"); + + if (parser->previous.length > 255) { + printError(parser, parser->previous, "Can't have a variable name longer than 255 characters"); + Toy_private_emitAstError(bucketHandle, rootHandle); + return; + } + + Toy_Token nameToken = parser->previous; + + //read the type specifier if present + Toy_ValueType varType = TOY_VALUE_ANY; + bool constant = false; + + if (match(parser, TOY_TOKEN_OPERATOR_COLON)) { + varType = readType(parser); + if (match(parser, TOY_TOKEN_KEYWORD_CONST)) { + constant = true; + } + } + + //build the name string & emit a var declare + Toy_String* nameStr = Toy_toStringLength(bucketHandle, nameToken.lexeme, nameToken.length); + Toy_private_emitAstVariableDeclaration(bucketHandle, &condBranch, nameStr, varType, constant, NULL); + + //continue to the 'in' keyword + consume(parser, TOY_TOKEN_KEYWORD_IN, "Expected 'in' inside 'for' condition"); makeExpr(bucketHandle, parser, &condBranch); + consume(parser, TOY_TOKEN_OPERATOR_PAREN_RIGHT, "Expected ')' after 'for' condition"); - //TODO: check for an iterable node + if (condBranch->type != TOY_AST_ITERABLE) { + printError(parser, parser->previous, "Expected 'in' in for condition"); + Toy_private_emitAstError(bucketHandle, rootHandle); + return; + } //{ thenBranch } makeDeclarationStmt(bucketHandle, parser, &thenBranch, true); @@ -1125,7 +1168,7 @@ static void makeStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** ro return; } - //TODO: import + //import? //print else if (match(parser, TOY_TOKEN_KEYWORD_PRINT)) { diff --git a/source/toy_vm.c b/source/toy_vm.c index e606926..4ef1579 100644 --- a/source/toy_vm.c +++ b/source/toy_vm.c @@ -33,6 +33,9 @@ static inline void fixAlignment(Toy_VM* vm) { vm->programCounter = (vm->programCounter + 3) & ~3; } +//forward declarations for delegations +static void processJump(Toy_VM* vm); + //instruction handlers static void processRead(Toy_VM* vm) { Toy_ValueType type = READ_BYTE(vm); @@ -470,9 +473,56 @@ static void processDuplicate(Toy_VM* vm) { } static void processEliminate(Toy_VM* vm) { - //discard the stack top - Toy_Value value = Toy_popStack(&vm->stack); - Toy_freeValue(value); + //discard the stack top, X times + unsigned int x = (unsigned int)READ_BYTE(vm); + for (unsigned int i = 0; i < x; i++) { + Toy_Value value = Toy_popStack(&vm->stack); + Toy_freeValue(value); + } +} + +static void processIterable(Toy_VM* vm) { + //access [-1] from [-2], incrementing [-1] afterwards + //if null, delegate to 'processJump' and exit + + Toy_Value counter = Toy_popStack(&vm->stack); + Toy_Value compound = Toy_popStack(&vm->stack); + + if (!TOY_VALUE_IS_INTEGER(counter)) { + fprintf(stderr, TOY_CC_ERROR "ERROR: Unknown counter type '%s' found in for loop, exiting\n" TOY_CC_RESET, Toy_getValueTypeAsCString(counter.type)); + exit(-1); + } + + if (TOY_VALUE_IS_ARRAY(compound)) { + Toy_Array* array = TOY_VALUE_AS_ARRAY(compound); + int index = TOY_VALUE_AS_INTEGER(counter); + + //check out-of-bounds + if (index < 0 || (unsigned int)index >= array->count) { + Toy_freeValue(counter); + Toy_freeValue(compound); + Toy_pushStack(&vm->stack, TOY_VALUE_FROM_NULL()); + processJump(vm); + return; + } + + //get the desired element + Toy_Value value = Toy_copyValue(&vm->memoryBucket, array->data[index]); + + //push everything back onto the stack + Toy_pushStack(&vm->stack, compound); + Toy_pushStack(&vm->stack, TOY_VALUE_FROM_INTEGER(index + 1)); + Toy_pushStack(&vm->stack, value); + + //cleanup & delegate + Toy_freeValue(compound); + Toy_freeValue(counter); + processJump(vm); + } + else { + fprintf(stderr, TOY_CC_ERROR "ERROR: Unknown value type '%s' found in for loop, exiting\n" TOY_CC_RESET, Toy_getValueTypeAsCString(compound.type)); + exit(-1); + } } static void processArithmetic(Toy_VM* vm, Toy_OpcodeType opcode) { @@ -682,6 +732,17 @@ static void processJump(Toy_VM* vm) { Toy_freeValue(value); return; } + + case TOY_OP_PARAM_JUMP_IF_NULL: { + Toy_Value value = Toy_popStack(&vm->stack); + if (TOY_VALUE_IS_NULL(value)) { + Toy_freeValue(value); + break; + } + + Toy_freeValue(value); + return; + } } //do the jump @@ -997,6 +1058,10 @@ static unsigned int process(Toy_VM* vm) { processEliminate(vm); break; + case TOY_OPCODE_ITERABLE: + processIterable(vm); + break; + //arithmetic instructions case TOY_OPCODE_ADD: case TOY_OPCODE_SUBTRACT: @@ -1060,7 +1125,6 @@ static unsigned int process(Toy_VM* vm) { processIndex(vm); break; - case TOY_OPCODE_ITERABLE: //tmp case TOY_OPCODE_UNUSED: case TOY_OPCODE_PASS: case TOY_OPCODE_ERROR: