From 813da3e1aa711e95fd0bfa94ee4db95ec4923dc1 Mon Sep 17 00:00:00 2001 From: Kayne Ruse Date: Thu, 21 May 2026 11:26:59 +1000 Subject: [PATCH] WIP Adding for-loop to parser & compiler --- source/toy_ast.c | 22 ++++++++++++++++++++ source/toy_ast.h | 18 +++++++++++++++++ source/toy_attributes.c | 3 ++- source/toy_compiler.c | 19 ++++++++++++++++++ source/toy_opcodes.h | 1 + source/toy_parser.c | 43 ++++++++++++++++++++++++++++++++++++---- source/toy_token_types.h | 2 +- source/toy_vm.c | 1 + 8 files changed, 103 insertions(+), 6 deletions(-) diff --git a/source/toy_ast.c b/source/toy_ast.c index 5269df9..14c412e 100644 --- a/source/toy_ast.c +++ b/source/toy_ast.c @@ -147,6 +147,16 @@ void Toy_private_emitAstWhileThen(Toy_Bucket** bucketHandle, Toy_Ast** astHandle (*astHandle) = tmp; } +void Toy_private_emitAstForThen(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* condBranch, Toy_Ast* thenBranch) { + Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast)); + + tmp->type = TOY_AST_FOR_THEN; + tmp->forThen.condBranch = condBranch; + tmp->forThen.thenBranch = thenBranch; + + (*astHandle) = tmp; +} + void Toy_private_emitAstBreak(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) { Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast)); @@ -244,6 +254,16 @@ void Toy_private_emitAstAttribute(Toy_Bucket** bucketHandle, Toy_Ast** astHandle (*astHandle) = tmp; } +void Toy_private_emitAstIterable(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* expr) { + Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast)); + + tmp->type = TOY_AST_ITERABLE; + tmp->iterable.left = (*astHandle); + tmp->iterable.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)); @@ -293,6 +313,7 @@ const char* Toy_private_getAstTypeAsCString(Toy_AstType type) { case TOY_AST_ASSERT: return "ASSERT"; case TOY_AST_IF_THEN_ELSE: return "IF_THEN_ELSE"; case TOY_AST_WHILE_THEN: return "WHILE_THEN"; + case TOY_AST_FOR_THEN: return "FOR_THEN"; case TOY_AST_BREAK: return "BREAK"; case TOY_AST_CONTINUE: return "CONTINUE"; case TOY_AST_RETURN: return "RETURN"; @@ -305,6 +326,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_ITERABLE: return "ITERABLE"; case TOY_AST_STACK_POP: return "STACK_POP"; diff --git a/source/toy_ast.h b/source/toy_ast.h index 839503d..1754dcd 100644 --- a/source/toy_ast.h +++ b/source/toy_ast.h @@ -22,6 +22,7 @@ typedef enum Toy_AstType { TOY_AST_ASSERT, TOY_AST_IF_THEN_ELSE, TOY_AST_WHILE_THEN, + TOY_AST_FOR_THEN, TOY_AST_BREAK, TOY_AST_CONTINUE, TOY_AST_RETURN, @@ -34,6 +35,7 @@ typedef enum Toy_AstType { TOY_AST_FN_DECLARE, TOY_AST_FN_INVOKE, TOY_AST_ATTRIBUTE, + TOY_AST_ITERABLE, TOY_AST_STACK_POP, //BUGFIX: force a single stack pop for expression statements @@ -171,6 +173,12 @@ typedef struct Toy_AstWhileThen { Toy_Ast* thenBranch; } Toy_AstWhileThen; +typedef struct Toy_AstForThen { + Toy_AstType type; + Toy_Ast* condBranch; + Toy_Ast* thenBranch; +} Toy_AstForThen; + typedef struct Toy_AstBreak { Toy_AstType type; } Toy_AstBreak; @@ -228,6 +236,12 @@ typedef struct Toy_AstAttribute { Toy_Ast* right; } Toy_AstAttribute; +typedef struct Toy_AstIterable { + Toy_AstType type; + Toy_Ast* left; + Toy_Ast* right; +} Toy_AstIterable; + typedef struct Toy_AstStackPop { Toy_AstType type; Toy_Ast* child; @@ -259,6 +273,7 @@ union Toy_Ast { //see 'test_ast.c' for bitness tests Toy_AstAssert assert; Toy_AstIfThenElse ifThenElse; Toy_AstWhileThen whileThen; + Toy_AstForThen forThen; Toy_AstBreak breakPoint; Toy_AstContinue continuePoint; Toy_AstReturn fnReturn; @@ -269,6 +284,7 @@ union Toy_Ast { //see 'test_ast.c' for bitness tests Toy_AstFnDeclare fnDeclare; Toy_AstFnInvoke fnInvoke; Toy_AstAttribute attribute; + Toy_AstIterable iterable; Toy_AstStackPop stackPop; Toy_AstPass pass; Toy_AstError error; @@ -290,6 +306,7 @@ void Toy_private_emitAstAggregate(Toy_Bucket** bucketHandle, Toy_Ast** astHandle void Toy_private_emitAstAssert(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* child, Toy_Ast* msg); void Toy_private_emitAstIfThenElse(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* condBranch, Toy_Ast* thenBranch, Toy_Ast* elseBranch); void Toy_private_emitAstWhileThen(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* condBranch, Toy_Ast* thenBranch); +void Toy_private_emitAstForThen(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* condBranch, Toy_Ast* thenBranch); void Toy_private_emitAstBreak(Toy_Bucket** bucketHandle, Toy_Ast** rootHandle); void Toy_private_emitAstContinue(Toy_Bucket** bucketHandle, Toy_Ast** rootHandle); void Toy_private_emitAstReturn(Toy_Bucket** bucketHandle, Toy_Ast** astHandle); @@ -302,6 +319,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_emitAstIterable(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* expr); void Toy_private_emitAstStackPop(Toy_Bucket** bucketHandle, Toy_Ast** astHandle); diff --git a/source/toy_attributes.c b/source/toy_attributes.c index 1ccd0e8..17a0ef9 100644 --- a/source/toy_attributes.c +++ b/source/toy_attributes.c @@ -91,6 +91,7 @@ static void attr_arrayPopBack(Toy_VM* vm, Toy_FunctionNative* self) { } static void attr_arrayForEach(Toy_VM* vm, Toy_FunctionNative* self) { + //URGENT: replace with for-loop (void)self; Toy_Value compound = Toy_popStack(&vm->stack); @@ -241,7 +242,7 @@ static void attr_tableForEach(Toy_VM* vm, Toy_FunctionNative* self) { (void)vm; (void)self; - //URGENT: attr_tableForEach + //URGENT: replace with for-loop } Toy_Value Toy_private_handleTableAttributes(Toy_VM* vm, Toy_Value compound, Toy_Value attribute) { diff --git a/source/toy_compiler.c b/source/toy_compiler.c index 809431a..66e67a3 100644 --- a/source/toy_compiler.c +++ b/source/toy_compiler.c @@ -718,6 +718,15 @@ static unsigned int writeInstructionWhileThen(Toy_Bytecode** mb, Toy_AstWhileThe return 0; } +static unsigned int writeInstructionForThen(Toy_Bytecode** mb, Toy_AstForThen ast) { + //URGENT: WIP + (void)mb; + (void)ast; + + (*mb)->panic = true; + return 1; +} + static unsigned int writeInstructionBreak(Toy_Bytecode** mb, Toy_AstBreak ast) { //unused (void)ast; @@ -1244,6 +1253,10 @@ static unsigned int writeBytecodeFromAst(Toy_Bytecode** mb, Toy_Ast* ast) { result += writeInstructionWhileThen(mb, ast->whileThen); break; + case TOY_AST_FOR_THEN: + result += writeInstructionForThen(mb, ast->forThen); + break; + case TOY_AST_BREAK: result += writeInstructionBreak(mb, ast->breakPoint); break; @@ -1284,6 +1297,12 @@ static unsigned int writeBytecodeFromAst(Toy_Bytecode** mb, Toy_Ast* ast) { result += writeInstructionAttribute(mb, ast->attribute); break; + case TOY_AST_ITERABLE: + //the 'in' keyword is only valid within a for-loop's condition + fprintf(stderr, TOY_CC_ERROR "COMPILER ERROR: the 'in' keyword is only valid within a for-loop's condition\n" TOY_CC_RESET); + (*mb)->panic = true; + 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 1a97c97..8aedbd4 100644 --- a/source/toy_opcodes.h +++ b/source/toy_opcodes.h @@ -12,6 +12,7 @@ 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 diff --git a/source/toy_parser.c b/source/toy_parser.c index 26b1bf1..9b8dcb8 100644 --- a/source/toy_parser.c +++ b/source/toy_parser.c @@ -122,6 +122,7 @@ static Toy_AstFlag aggregate(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_ 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); +static Toy_AstFlag iterable(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle); //precedence definitions static ParsingTuple parsingRulesetTable[] = { @@ -156,7 +157,7 @@ static ParsingTuple parsingRulesetTable[] = { {PREC_NONE,NULL,NULL},// TOY_TOKEN_KEYWORD_FUNCTION, {PREC_NONE,NULL,NULL},// TOY_TOKEN_KEYWORD_IF, {PREC_NONE,NULL,NULL},// TOY_TOKEN_KEYWORD_IMPORT, - {PREC_NONE,NULL,NULL},// TOY_TOKEN_KEYWORD_IN, + {PREC_CALL,NULL,iterable},// TOY_TOKEN_KEYWORD_IN, {PREC_NONE,NULL,NULL},// TOY_TOKEN_KEYWORD_OF, {PREC_NONE,NULL,NULL},// TOY_TOKEN_KEYWORD_PASS, {PREC_NONE,NULL,NULL},// TOY_TOKEN_KEYWORD_PRINT, @@ -760,6 +761,19 @@ static Toy_AstFlag attribute(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_ } } +static Toy_AstFlag iterable(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) { + //infix must advance + advance(parser); + + Toy_Ast* expr = NULL; + parsePrecedence(bucketHandle, parser, &expr, PREC_CALL); + Toy_private_emitAstIterable(bucketHandle, rootHandle, expr); + + //TODO: check for var declare without assignment + + 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 @@ -865,7 +879,7 @@ static void makeIfThenElseStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, To makeExpr(bucketHandle, parser, &condBranch); consume(parser, TOY_TOKEN_OPERATOR_PAREN_RIGHT, "Expected ')' after 'if' condition"); - // { thenBranch } + //{ thenBranch } makeDeclarationStmt(bucketHandle, parser, &thenBranch, true); //else { elseBranch } @@ -885,12 +899,29 @@ static void makeWhileStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast makeExpr(bucketHandle, parser, &condBranch); consume(parser, TOY_TOKEN_OPERATOR_PAREN_RIGHT, "Expected ')' after 'while' condition"); - // { thenBranch } + //{ thenBranch } makeDeclarationStmt(bucketHandle, parser, &thenBranch, true); Toy_private_emitAstWhileThen(bucketHandle, rootHandle, condBranch, thenBranch); } +static void makeForStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) { + Toy_Ast* condBranch = NULL; + Toy_Ast* thenBranch = NULL; + + //for (condBranch) + consume(parser, TOY_TOKEN_OPERATOR_PAREN_LEFT, "Expected '(' after 'for' keyword"); + makeExpr(bucketHandle, parser, &condBranch); + consume(parser, TOY_TOKEN_OPERATOR_PAREN_RIGHT, "Expected ')' after 'for' condition"); + + //TODO: check for an iterable node + + //{ thenBranch } + makeDeclarationStmt(bucketHandle, parser, &thenBranch, true); + + Toy_private_emitAstForThen(bucketHandle, rootHandle, condBranch, thenBranch); +} + static void makeBreakStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) { Toy_private_emitAstBreak(bucketHandle, rootHandle); consume(parser, TOY_TOKEN_OPERATOR_SEMICOLON, "Expected ';' at the end of break statement"); @@ -1070,7 +1101,11 @@ static void makeStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** ro return; } - //URGENT: for-pre-clause-post-then + //for + else if (match(parser, TOY_TOKEN_KEYWORD_FOR)) { + makeForStmt(bucketHandle, parser, rootHandle); + return; + } //break else if (match(parser, TOY_TOKEN_KEYWORD_BREAK)) { diff --git a/source/toy_token_types.h b/source/toy_token_types.h index e43564a..a7379fd 100644 --- a/source/toy_token_types.h +++ b/source/toy_token_types.h @@ -29,7 +29,7 @@ typedef enum Toy_TokenType { TOY_TOKEN_KEYWORD_DO, //unused TOY_TOKEN_KEYWORD_ELSE, TOY_TOKEN_KEYWORD_EXPORT, //unused - TOY_TOKEN_KEYWORD_FOR, //unused + TOY_TOKEN_KEYWORD_FOR, TOY_TOKEN_KEYWORD_FOREACH, //unused TOY_TOKEN_KEYWORD_FUNCTION, //remapped 'fn' TOY_TOKEN_KEYWORD_IF, diff --git a/source/toy_vm.c b/source/toy_vm.c index 9f09b1f..e606926 100644 --- a/source/toy_vm.c +++ b/source/toy_vm.c @@ -1060,6 +1060,7 @@ 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: