From 6d239748f4695e44ba4a5ed517ff138b079e117a Mon Sep 17 00:00:00 2001 From: Nikolaj Riel Date: Sun, 25 May 2025 09:00:29 +0200 Subject: [PATCH 1/8] Working for-loop + while tokens added. --- src/lexer.c | 7 +++- src/lexer.h | 3 ++ src/parser.c | 100 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 109 insertions(+), 1 deletion(-) diff --git a/src/lexer.c b/src/lexer.c index 6db4781..781e63d 100644 --- a/src/lexer.c +++ b/src/lexer.c @@ -28,7 +28,8 @@ static const KeywordEntry kKeywordMap[] = { {"endif", kTokenEndif}, {"for", kTokenFor}, {"endfor", kTokenEndfor}, {"local", kTokenLocal}, {"func", kTokenFunc}, {"endfunc", kTokenEndfunc}, {"ret", kTokenRet}, {"int", kTokenInt}, {"float", kTokenFloat}, - {"str", kTokenStr}, {"bool", kTokenBool}, {"array", kTokenArray}}; + {"str", kTokenStr}, {"bool", kTokenBool}, {"array", kTokenArray}, + {"while", kTokenWhile}/*, {"endwhile", kTokenEndwhile}*/}; #ifdef __CC65__ static const size_t kKeywordCount = sizeof(kKeywordMap) / sizeof(KeywordEntry); @@ -192,6 +193,10 @@ static bool IsCharacter() { case ':': token.type = kTokenColon; + break; + case ';': + token.type = kTokenSemicolon; + break; case '>': IncrementProgramBufferIndex(); diff --git a/src/lexer.h b/src/lexer.h index cb650a3..810ff87 100644 --- a/src/lexer.h +++ b/src/lexer.h @@ -47,6 +47,7 @@ typedef enum TokenType { kTokenDot, kTokenComma, kTokenColon, + kTokenSemicolon, kTokenAssign, kTokenEquals, kTokenNot, @@ -63,6 +64,8 @@ typedef enum TokenType { kTokenEndif, kTokenFor, kTokenEndfor, + kTokenWhile, + KTokenEndwhile, kTokenPrint, kTokenLocal, kTokenBoolean, diff --git a/src/parser.c b/src/parser.c index 720659a..c2828aa 100644 --- a/src/parser.c +++ b/src/parser.c @@ -862,6 +862,100 @@ static void ParseIdentifierStatement(const char* const identifier_name) { puts("Error: Unexpected token after identifier."); } +// NOLINTNEXTLINE(misc-no-recursion) +static void ParseForStatement() { + size_t pending_condition_slot = 0; + size_t loop_start_address = 0; + size_t loop_exit_address = 0; + + if (!ExpectToken(1, kTokenLeftParenthesis)) { + return; + } + + // Parse variable declaration + // initializer example: i: int = 0 + char identifier_name[kIdentifierNameLength]; + ExtractIdentifierName(identifier_name); + + if (!ExpectToken(1, kTokenIdentifier)) { + return; + } + + if (!ExpectToken(1, kTokenColon)) { + return; + } + + ParseVariableDeclaration(identifier_name); + + if (!ExpectToken(1, kTokenSemicolon)) { + puts("That's yap!: You're missing a semicolon after the initializer."); + return; + } + + // Mark condition check address + loop_start_address = instruction_address; + + // Parse loop + // condition example: i < 3 + ParseExpression(); + + EmitByte(kOpJumpIfFalse); + pending_condition_slot = instruction_address; + EmitByte(0); + + if (!ExpectToken(1, kTokenSemicolon)) { + puts("That's yap!: You're missing a semicolon after the condition."); + return; + } + + // Store increment start location + size_t increment_start_address = instruction_address; + + // Parse increment + // increment example: i = i + 1 + ExtractIdentifierName(identifier_name); + + if (!ExpectToken(1, kTokenIdentifier)) { + return; + } + + if (!ExpectToken(1, kTokenAssign)) { + return; + } + + ParseVariableAssignment(identifier_name); + + if (!ExpectToken(1, kTokenRightParenthesis)) { + return; + } + + // Jump from end of increment to body of loop + EmitByte(kOpJump); + loop_exit_address = instruction_address; + EmitByte(0); + + // Loop increment jump target + EmitByte(kOpJump); + EmitByte(increment_start_address); + + // Patch jump into loop body + instructions[loop_exit_address] = instruction_address; + + // Parse loop body + while (token.type != kTokenEndfor && token.type != kTokenEof) { + ParseStatement(); + } + + // After body, jump back to condition + EmitByte(kOpJump); + EmitByte(loop_start_address); + + // Pending false jump to exit loop + instructions[pending_condition_slot] = instruction_address; + + ExpectToken(1, kTokenEndfor); +} + // NOLINTNEXTLINE(misc-no-recursion) static void ParseStatement() { char identifier_name[kIdentifierNameLength]; @@ -884,6 +978,12 @@ static void ParseStatement() { return; } + if (AcceptToken(1, kTokenFor)) { + ParseForStatement(); + + return; + } + ExtractIdentifierName(identifier_name); if (AcceptToken(1, kTokenIdentifier)) { From 9ca7f78d6929fbf6f292261e0cacb1d501805fa8 Mon Sep 17 00:00:00 2001 From: Nikolaj Riel Date: Sun, 25 May 2025 09:33:28 +0200 Subject: [PATCH 2/8] Working while-loop, local testing. --- src/lexer.c | 2 +- src/lexer.h | 2 +- src/parser.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/lexer.c b/src/lexer.c index 781e63d..ff8ec1d 100644 --- a/src/lexer.c +++ b/src/lexer.c @@ -29,7 +29,7 @@ static const KeywordEntry kKeywordMap[] = { {"local", kTokenLocal}, {"func", kTokenFunc}, {"endfunc", kTokenEndfunc}, {"ret", kTokenRet}, {"int", kTokenInt}, {"float", kTokenFloat}, {"str", kTokenStr}, {"bool", kTokenBool}, {"array", kTokenArray}, - {"while", kTokenWhile}/*, {"endwhile", kTokenEndwhile}*/}; + {"while", kTokenWhile}, {"endwhile", kTokenEndwhile}}; #ifdef __CC65__ static const size_t kKeywordCount = sizeof(kKeywordMap) / sizeof(KeywordEntry); diff --git a/src/lexer.h b/src/lexer.h index 810ff87..808672c 100644 --- a/src/lexer.h +++ b/src/lexer.h @@ -65,7 +65,7 @@ typedef enum TokenType { kTokenFor, kTokenEndfor, kTokenWhile, - KTokenEndwhile, + kTokenEndwhile, kTokenPrint, kTokenLocal, kTokenBoolean, diff --git a/src/parser.c b/src/parser.c index c2828aa..4160c28 100644 --- a/src/parser.c +++ b/src/parser.c @@ -956,6 +956,46 @@ static void ParseForStatement() { ExpectToken(1, kTokenEndfor); } +// NOLINTNEXTLINE(misc-no-recursion) +static void ParseWhileStatement() { + size_t loop_start_index = 0; + size_t pending_loop_exit_slot = 0; + + // Mark where the loop starts + loop_start_index = instruction_address; + + // Parse condition + // condition example: i < 3 + if (!ExpectToken(1, kTokenLeftParenthesis)) { + return; + } + + ParseExpression(); + + if (!ExpectToken(1, kTokenRightParenthesis)) { + return; + } + + // Emit conditional jump to exit if false + EmitByte(kOpJumpIfFalse); + pending_loop_exit_slot = instruction_address; + EmitByte(0); // placeholder to be patched + + // Parse loop body + while (token.type != kTokenEndwhile && token.type != kTokenEof) { + ParseStatement(); + } + + // Jump back to start of loop + EmitByte(kOpJump); + EmitByte(loop_start_index); + + // Waiting for instruction jump to jump to loop exit + instructions[pending_loop_exit_slot] = instruction_address; + + ExpectToken(1, kTokenEndwhile); +} + // NOLINTNEXTLINE(misc-no-recursion) static void ParseStatement() { char identifier_name[kIdentifierNameLength]; @@ -984,6 +1024,12 @@ static void ParseStatement() { return; } + if (AcceptToken(1, kTokenWhile)) { + ParseWhileStatement(); + + return; + } + ExtractIdentifierName(identifier_name); if (AcceptToken(1, kTokenIdentifier)) { From 5518ba6b7c62b0f42f0e5cbe70aba6ba0eef7757 Mon Sep 17 00:00:00 2001 From: Nikolaj Riel Date: Sun, 25 May 2025 19:33:50 +0200 Subject: [PATCH 3/8] While-loop tests pass. --- tests/main.c | 4 +++ tests/vm_test.c | 89 +++++++++++++++++++++++++++++++++++++++++++++++++ tests/vm_test.h | 7 ++++ 3 files changed, 100 insertions(+) diff --git a/tests/main.c b/tests/main.c index c9728a0..b967049 100644 --- a/tests/main.c +++ b/tests/main.c @@ -93,5 +93,9 @@ int main() { RUN_TEST(TestIfLessThanComparison); RUN_TEST(TestElse); + // Loops + //RUN_TEST(TestForLoopExecutesThreeTimes); + RUN_TEST(TestWhileLoopExecutesThreeTimes); + RUN_TEST(TestNestedWhileLoops); return UNITY_END(); } diff --git a/tests/vm_test.c b/tests/vm_test.c index 37e785d..3d27dc5 100644 --- a/tests/vm_test.c +++ b/tests/vm_test.c @@ -101,3 +101,92 @@ void TestElse() { RunVm(); } + +// Loops testing + +/*void TestForLoopExecutesThreeTimes() { + int print_occurences = 0; + + FillProgramBufferAndParse( + "for(i: int = 0; i < 3; i = i + 1)\n" + " print(i)\n" + "endfor"); + + bool saw_jump = false; + bool saw_jump_if_false = false; + + for (size_t i = 0; i < kInstructionsSize; ++i) { + if (instructions[i] == kOpPrint) { + print_occurences++; + } + if (instructions[i] == kOpJump) { + saw_jump = true; + } + if (instructions[i] == kOpJumpIfFalse) { + saw_jump_if_false = true; + } + } + + TEST_ASSERT_TRUE_MESSAGE(saw_jump, "Missing kOpJump for loop iteration"); + TEST_ASSERT_TRUE_MESSAGE(saw_jump_if_false, + "Missing kOpJumpIfFalse for loop condition"); + TEST_ASSERT_EQUAL_INT_MESSAGE(3, print_occurences, "Loop did not print 3 times"); + + RunVm(); +} +*/ +void TestWhileLoopExecutesThreeTimes() { + FillProgramBufferAndParse( + "i: int = 0\n" + "while(i < 3)\n" + " print(i)\n" + " i = i + 1\n" + "endwhile"); + + int print_count = 0; + bool saw_jump_if_false = false; + bool saw_jump = false; + + for (size_t i = 0; i < kInstructionsSize; ++i) { + if (instructions[i] == kOpPrint) { + print_count++; + } + if (instructions[i] == kOpJumpIfFalse) { + saw_jump_if_false = true; + } + if (instructions[i] == kOpJump) { + saw_jump = true; + } + } + + TEST_ASSERT_TRUE_MESSAGE(saw_jump_if_false, "Missing kOpJumpIfFalse for while"); + TEST_ASSERT_TRUE_MESSAGE(saw_jump, "Missing kOpJump to repeat loop"); + TEST_ASSERT_EQUAL_INT_MESSAGE(1, print_count, + "Expected 1 print instruction in bytecode"); + + RunVm(); +} + +void TestNestedWhileLoops() { + // Expected output should be: + // 0 + // 1 + // 0 + // 1 + + FillProgramBufferAndParse( + "i: int = 0\n" + "j: int = 0\n" + "while(i < 2)\n" + " j = 0\n" + " while(j < 2)\n" + " print(j)\n" + " j = j + 1\n" + " endwhile\n" + " i = i + 1\n" + "endwhile"); + + RunVm(); +} + + diff --git a/tests/vm_test.h b/tests/vm_test.h index 2830237..2bfeb3b 100644 --- a/tests/vm_test.h +++ b/tests/vm_test.h @@ -12,4 +12,11 @@ void TestIfGreaterThanComparison(); void TestIfLessThanComparison(); void TestElse(); +// Loops +//void TestForLoopExecutesThreeTimes(); + +void TestWhileLoopExecutesThreeTimes(); +void TestNestedWhileLoops(); + + #endif // CONDITIONALS_TEST_H From d312b28274e6a95655e02bb516493718cc9928a2 Mon Sep 17 00:00:00 2001 From: Nikolaj Riel Date: Tue, 27 May 2025 11:06:03 +0200 Subject: [PATCH 4/8] Nothing of note, empty space showing as modified. --- src/vm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vm.c b/src/vm.c index 4223c08..7690d63 100644 --- a/src/vm.c +++ b/src/vm.c @@ -758,4 +758,4 @@ void RunVm() { } } } -} +} \ No newline at end of file From baaa08b9e1bae12ef06fa39ba9e283e689d76a19 Mon Sep 17 00:00:00 2001 From: Nikolaj Riel Date: Wed, 25 Jun 2025 00:43:07 +0200 Subject: [PATCH 5/8] Ran make format. --- src/lexer.c | 15 +++++++++------ tests/main.c | 2 +- tests/vm_test.c | 8 ++++---- tests/vm_test.h | 3 +-- 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/lexer.c b/src/lexer.c index 8eb36ba..1a79a20 100644 --- a/src/lexer.c +++ b/src/lexer.c @@ -24,12 +24,15 @@ typedef struct KeywordEntry { } KeywordEntry; static const KeywordEntry kKeywordMap[] = { - {"print", kTokenPrint}, {"if", kTokenIf}, {"else", kTokenElse}, - {"endif", kTokenEndif}, {"for", kTokenFor}, {"endfor", kTokenEndfor}, - {"local", kTokenLocal}, {"func", kTokenFunc}, {"endfunc", kTokenEndfunc}, - {"ret", kTokenRet}, {"int", kTokenInt}, {"float", kTokenFloat}, - {"str", kTokenStr}, {"bool", kTokenBool}, {"array", kTokenArray}, - {"while", kTokenWhile}, {"endwhile", kTokenEndwhile}}; + {"print", kTokenPrint}, {"if", kTokenIf}, + {"else", kTokenElse}, {"endif", kTokenEndif}, + {"for", kTokenFor}, {"endfor", kTokenEndfor}, + {"local", kTokenLocal}, {"func", kTokenFunc}, + {"endfunc", kTokenEndfunc}, {"ret", kTokenRet}, + {"int", kTokenInt}, {"float", kTokenFloat}, + {"str", kTokenStr}, {"bool", kTokenBool}, + {"array", kTokenArray}, {"while", kTokenWhile}, + {"endwhile", kTokenEndwhile}}; #ifdef __CC65__ static const size_t kKeywordCount = sizeof(kKeywordMap) / sizeof(KeywordEntry); diff --git a/tests/main.c b/tests/main.c index 85db71b..46590d3 100644 --- a/tests/main.c +++ b/tests/main.c @@ -118,7 +118,7 @@ int main() { // RUN_TEST(Example10); // Loops - //RUN_TEST(TestForLoopExecutesThreeTimes); + // RUN_TEST(TestForLoopExecutesThreeTimes); RUN_TEST(TestWhileLoopExecutesThreeTimes); RUN_TEST(TestNestedWhileLoops); return UNITY_END(); diff --git a/tests/vm_test.c b/tests/vm_test.c index bfbe1f0..d36de24 100644 --- a/tests/vm_test.c +++ b/tests/vm_test.c @@ -358,7 +358,8 @@ void Example10() { TEST_ASSERT_TRUE_MESSAGE(saw_jump, "Missing kOpJump for loop iteration"); TEST_ASSERT_TRUE_MESSAGE(saw_jump_if_false, "Missing kOpJumpIfFalse for loop condition"); - TEST_ASSERT_EQUAL_INT_MESSAGE(3, print_occurences, "Loop did not print 3 times"); + TEST_ASSERT_EQUAL_INT_MESSAGE(3, print_occurences, "Loop did not print 3 +times"); RunVm(); } @@ -387,7 +388,8 @@ void TestWhileLoopExecutesThreeTimes() { } } - TEST_ASSERT_TRUE_MESSAGE(saw_jump_if_false, "Missing kOpJumpIfFalse for while"); + TEST_ASSERT_TRUE_MESSAGE(saw_jump_if_false, + "Missing kOpJumpIfFalse for while"); TEST_ASSERT_TRUE_MESSAGE(saw_jump, "Missing kOpJump to repeat loop"); TEST_ASSERT_EQUAL_INT_MESSAGE(1, print_count, "Expected 1 print instruction in bytecode"); @@ -416,5 +418,3 @@ void TestNestedWhileLoops() { RunVm(); } - - diff --git a/tests/vm_test.h b/tests/vm_test.h index 68aac58..69012cd 100644 --- a/tests/vm_test.h +++ b/tests/vm_test.h @@ -32,10 +32,9 @@ void Example9(); void Example10(); // Loops -//void TestForLoopExecutesThreeTimes(); +// void TestForLoopExecutesThreeTimes(); void TestWhileLoopExecutesThreeTimes(); void TestNestedWhileLoops(); - #endif // CONDITIONALS_TEST_H From 52aeacbb5f8424c4837d0ad403f9b2c131f102ab Mon Sep 17 00:00:00 2001 From: Nikolaj Riel Date: Wed, 25 Jun 2025 01:38:09 +0200 Subject: [PATCH 6/8] Changed some lint errors from ParseForStatement. --- src/parser.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/parser.c b/src/parser.c index 4160c28..5b363d8 100644 --- a/src/parser.c +++ b/src/parser.c @@ -867,6 +867,8 @@ static void ParseForStatement() { size_t pending_condition_slot = 0; size_t loop_start_address = 0; size_t loop_exit_address = 0; + size_t increment_start_address = 0; + char identifier_name[kIdentifierNameLength]; if (!ExpectToken(1, kTokenLeftParenthesis)) { return; @@ -874,7 +876,6 @@ static void ParseForStatement() { // Parse variable declaration // initializer example: i: int = 0 - char identifier_name[kIdentifierNameLength]; ExtractIdentifierName(identifier_name); if (!ExpectToken(1, kTokenIdentifier)) { @@ -909,7 +910,7 @@ static void ParseForStatement() { } // Store increment start location - size_t increment_start_address = instruction_address; + increment_start_address = instruction_address; // Parse increment // increment example: i = i + 1 From f1b25b371a3aa237a2c2323805b2cced05f86e31 Mon Sep 17 00:00:00 2001 From: Nikolaj Riel Date: Wed, 25 Jun 2025 04:32:30 +0200 Subject: [PATCH 7/8] Working for-loop with a passing test. Change made to EmitHalt(). --- src/parser.c | 59 +++++++++++++++++++++++-------------------------- src/vm.c | 5 +++-- tests/main.c | 2 +- tests/vm_test.c | 26 +++++++++++----------- tests/vm_test.h | 3 +-- 5 files changed, 46 insertions(+), 49 deletions(-) diff --git a/src/parser.c b/src/parser.c index 5b363d8..81376ff 100644 --- a/src/parser.c +++ b/src/parser.c @@ -864,10 +864,11 @@ static void ParseIdentifierStatement(const char* const identifier_name) { // NOLINTNEXTLINE(misc-no-recursion) static void ParseForStatement() { - size_t pending_condition_slot = 0; - size_t loop_start_address = 0; - size_t loop_exit_address = 0; + size_t jump_if_false_patch = 0; + size_t loop_condition_address = 0; size_t increment_start_address = 0; + size_t jump_after_increment_patch = 0; + size_t after_loop_address = 0; char identifier_name[kIdentifierNameLength]; if (!ExpectToken(1, kTokenLeftParenthesis)) { @@ -875,9 +876,8 @@ static void ParseForStatement() { } // Parse variable declaration - // initializer example: i: int = 0 + // Initializer example: i: int = 0 ExtractIdentifierName(identifier_name); - if (!ExpectToken(1, kTokenIdentifier)) { return; } @@ -893,29 +893,28 @@ static void ParseForStatement() { return; } - // Mark condition check address - loop_start_address = instruction_address; - - // Parse loop - // condition example: i < 3 - ParseExpression(); + // Parse condition address + loop_condition_address = instruction_address; + ParseExpression(); // Condition example: i < 3 EmitByte(kOpJumpIfFalse); - pending_condition_slot = instruction_address; - EmitByte(0); + jump_if_false_patch = instruction_address; + EmitByte(0); // patched after loop body if (!ExpectToken(1, kTokenSemicolon)) { puts("That's yap!: You're missing a semicolon after the condition."); return; } - // Store increment start location + // Parse increment & store increment skip address + // Increment example: i = i + 1 + EmitByte(kOpJump); + jump_after_increment_patch = instruction_address; + EmitByte(0); // patched to skip increment on false condition + increment_start_address = instruction_address; - // Parse increment - // increment example: i = i + 1 ExtractIdentifierName(identifier_name); - if (!ExpectToken(1, kTokenIdentifier)) { return; } @@ -930,31 +929,29 @@ static void ParseForStatement() { return; } - // Jump from end of increment to body of loop + // Jump from end of increment back to condition EmitByte(kOpJump); - loop_exit_address = instruction_address; - EmitByte(0); - - // Loop increment jump target - EmitByte(kOpJump); - EmitByte(increment_start_address); + EmitByte(loop_condition_address); - // Patch jump into loop body - instructions[loop_exit_address] = instruction_address; + // Patch earlier jump to skip increment on first entry + instructions[jump_after_increment_patch] = instruction_address; // Parse loop body while (token.type != kTokenEndfor && token.type != kTokenEof) { - ParseStatement(); + ParseStatement(); // Body example: print(i) } - // After body, jump back to condition + // Jump from body to increment EmitByte(kOpJump); - EmitByte(loop_start_address); + EmitByte(increment_start_address); - // Pending false jump to exit loop - instructions[pending_condition_slot] = instruction_address; + // === PATCHING === + after_loop_address = instruction_address; + instructions[jump_if_false_patch] = after_loop_address; ExpectToken(1, kTokenEndfor); + + EmitHalt(); } // NOLINTNEXTLINE(misc-no-recursion) diff --git a/src/vm.c b/src/vm.c index 7690d63..3b1c414 100644 --- a/src/vm.c +++ b/src/vm.c @@ -149,9 +149,10 @@ void EmitByte(const unsigned char byte) { } void EmitHalt() { - if (kOpHalt != instructions[instruction_address - 1]) { + /* if (kOpHalt != instructions[instruction_address - 1]) { EmitByte(kOpHalt); - } + }*/ + EmitByte(kOpHalt); } void RemoveHalt() { diff --git a/tests/main.c b/tests/main.c index 46590d3..9b2625a 100644 --- a/tests/main.c +++ b/tests/main.c @@ -118,7 +118,7 @@ int main() { // RUN_TEST(Example10); // Loops - // RUN_TEST(TestForLoopExecutesThreeTimes); + RUN_TEST(TestForLoopExecutesThreeTimes); RUN_TEST(TestWhileLoopExecutesThreeTimes); RUN_TEST(TestNestedWhileLoops); return UNITY_END(); diff --git a/tests/vm_test.c b/tests/vm_test.c index d36de24..534e739 100644 --- a/tests/vm_test.c +++ b/tests/vm_test.c @@ -332,38 +332,38 @@ void Example10() { // Loops testing -/*void TestForLoopExecutesThreeTimes() { - int print_occurences = 0; - +void TestForLoopExecutesThreeTimes(void) { FillProgramBufferAndParse( "for(i: int = 0; i < 3; i = i + 1)\n" " print(i)\n" "endfor"); - bool saw_jump = false; + int print_count = 0; + int jump_count = 0; bool saw_jump_if_false = false; for (size_t i = 0; i < kInstructionsSize; ++i) { if (instructions[i] == kOpPrint) { - print_occurences++; - } - if (instructions[i] == kOpJump) { - saw_jump = true; + print_count++; } if (instructions[i] == kOpJumpIfFalse) { saw_jump_if_false = true; } + if (instructions[i] == kOpJump) { + jump_count++; + } } - TEST_ASSERT_TRUE_MESSAGE(saw_jump, "Missing kOpJump for loop iteration"); TEST_ASSERT_TRUE_MESSAGE(saw_jump_if_false, - "Missing kOpJumpIfFalse for loop condition"); - TEST_ASSERT_EQUAL_INT_MESSAGE(3, print_occurences, "Loop did not print 3 -times"); + "Missing kOpJumpIfFalse for for-loop condition"); + TEST_ASSERT_TRUE_MESSAGE(jump_count >= 2, + "Expected at least two kOpJump instructions (one to body, one to increment)"); + TEST_ASSERT_EQUAL_INT_MESSAGE(1, print_count, + "Expected 1 print instruction in bytecode"); RunVm(); } -*/ + void TestWhileLoopExecutesThreeTimes() { FillProgramBufferAndParse( "i: int = 0\n" diff --git a/tests/vm_test.h b/tests/vm_test.h index 69012cd..b34a3c3 100644 --- a/tests/vm_test.h +++ b/tests/vm_test.h @@ -32,8 +32,7 @@ void Example9(); void Example10(); // Loops -// void TestForLoopExecutesThreeTimes(); - +void TestForLoopExecutesThreeTimes(void); void TestWhileLoopExecutesThreeTimes(); void TestNestedWhileLoops(); From 50f0596c166be0f19c354757fb73d5d9790b8225 Mon Sep 17 00:00:00 2001 From: Nikolaj Riel Date: Wed, 25 Jun 2025 04:36:07 +0200 Subject: [PATCH 8/8] Make lint + format. --- src/parser.c | 2 +- src/vm.c | 6 +++--- tests/vm_test.c | 3 ++- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/parser.c b/src/parser.c index 81376ff..1a80b9d 100644 --- a/src/parser.c +++ b/src/parser.c @@ -938,7 +938,7 @@ static void ParseForStatement() { // Parse loop body while (token.type != kTokenEndfor && token.type != kTokenEof) { - ParseStatement(); // Body example: print(i) + ParseStatement(); // Body example: print(i) } // Jump from body to increment diff --git a/src/vm.c b/src/vm.c index 3b1c414..f56b479 100644 --- a/src/vm.c +++ b/src/vm.c @@ -149,9 +149,9 @@ void EmitByte(const unsigned char byte) { } void EmitHalt() { - /* if (kOpHalt != instructions[instruction_address - 1]) { - EmitByte(kOpHalt); - }*/ + /* if (kOpHalt != instructions[instruction_address - 1]) { + EmitByte(kOpHalt); + }*/ EmitByte(kOpHalt); } diff --git a/tests/vm_test.c b/tests/vm_test.c index 534e739..a2728ee 100644 --- a/tests/vm_test.c +++ b/tests/vm_test.c @@ -357,7 +357,8 @@ void TestForLoopExecutesThreeTimes(void) { TEST_ASSERT_TRUE_MESSAGE(saw_jump_if_false, "Missing kOpJumpIfFalse for for-loop condition"); TEST_ASSERT_TRUE_MESSAGE(jump_count >= 2, - "Expected at least two kOpJump instructions (one to body, one to increment)"); + "Expected at least two kOpJump instructions (one to " + "body, one to increment)"); TEST_ASSERT_EQUAL_INT_MESSAGE(1, print_count, "Expected 1 print instruction in bytecode");