From a882b741509943df5e82dcbd7c67834618307454 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 31 Mar 2026 20:09:48 +0000 Subject: [PATCH 1/3] Initial plan From 82d14b007710ce7b8a1625f6a523851a262ad4c4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 31 Mar 2026 20:16:36 +0000 Subject: [PATCH 2/3] Fix for(;;) NullReferenceException: handle null expression_statement in FOR rules - Update 4 FOR grammar rules in CParser.jay to use reference type null checking (`$4 is ExpressionStatement es ? es.Expression : null`) - Make ForStatement.ContinueExpression nullable and skip condition emission when null (infinite loop until break) - Regenerate parser via prebuild.sh - Add tests for for(;;), for(i=0;;i++), for(i=0;i<10;), for(;i<5;i++) Agent-Logs-Url: https://github.com/praeclarum/CLanguage/sessions/4fd85394-830c-4edd-b5a1-71623366c29b Co-authored-by: praeclarum <323548+praeclarum@users.noreply.github.com> --- CLanguage/Parser/CParser.cs | 8 ++--- CLanguage/Parser/CParser.jay | 8 ++--- CLanguage/Syntax/ForStatement.cs | 14 ++++---- CLanguageTests/InterpreterTests.cs | 52 ++++++++++++++++++++++++++++++ 4 files changed, 68 insertions(+), 14 deletions(-) diff --git a/CLanguage/Parser/CParser.cs b/CLanguage/Parser/CParser.cs index df7b83f..602da46 100644 --- a/CLanguage/Parser/CParser.cs +++ b/CLanguage/Parser/CParser.cs @@ -1825,25 +1825,25 @@ internal Object yyparse (yyParser.yyInput yyLex) case 292: #line 1115 "CParser.jay" { - yyVal = new ForStatement((Statement)yyVals[-3+yyTop], ((ExpressionStatement)yyVals[-2+yyTop]).Expression, ((Statement)yyVals[0+yyTop]).ToBlock ()); + yyVal = new ForStatement((Statement)yyVals[-3+yyTop], yyVals[-2+yyTop] is ExpressionStatement es4a ? es4a.Expression : null, ((Statement)yyVals[0+yyTop]).ToBlock ()); } break; case 293: #line 1119 "CParser.jay" { - yyVal = new ForStatement((Statement)yyVals[-4+yyTop], ((ExpressionStatement)yyVals[-3+yyTop]).Expression, (Expression)yyVals[-2+yyTop], ((Statement)yyVals[0+yyTop]).ToBlock ()); + yyVal = new ForStatement((Statement)yyVals[-4+yyTop], yyVals[-3+yyTop] is ExpressionStatement es4b ? es4b.Expression : null, (Expression)yyVals[-2+yyTop], ((Statement)yyVals[0+yyTop]).ToBlock ()); } break; case 294: #line 1123 "CParser.jay" { - yyVal = new ForStatement((Statement)yyVals[-3+yyTop], ((ExpressionStatement)yyVals[-2+yyTop]).Expression, ((Statement)yyVals[0+yyTop]).ToBlock ()); + yyVal = new ForStatement((Statement)yyVals[-3+yyTop], yyVals[-2+yyTop] is ExpressionStatement es4c ? es4c.Expression : null, ((Statement)yyVals[0+yyTop]).ToBlock ()); } break; case 295: #line 1127 "CParser.jay" { - yyVal = new ForStatement((Statement)yyVals[-4+yyTop], ((ExpressionStatement)yyVals[-3+yyTop]).Expression, (Expression)yyVals[-2+yyTop], ((Statement)yyVals[0+yyTop]).ToBlock ()); + yyVal = new ForStatement((Statement)yyVals[-4+yyTop], yyVals[-3+yyTop] is ExpressionStatement es4d ? es4d.Expression : null, (Expression)yyVals[-2+yyTop], ((Statement)yyVals[0+yyTop]).ToBlock ()); } break; case 297: diff --git a/CLanguage/Parser/CParser.jay b/CLanguage/Parser/CParser.jay index 415cf65..2949f22 100644 --- a/CLanguage/Parser/CParser.jay +++ b/CLanguage/Parser/CParser.jay @@ -1111,19 +1111,19 @@ iteration_statement } | FOR '(' expression_statement expression_statement ')' statement { - $$ = new ForStatement((Statement)$3, ((ExpressionStatement)$4).Expression, ((Statement)$6).ToBlock ()); + $$ = new ForStatement((Statement)$3, $4 is ExpressionStatement es4a ? es4a.Expression : null, ((Statement)$6).ToBlock ()); } | FOR '(' expression_statement expression_statement expression ')' statement { - $$ = new ForStatement((Statement)$3, ((ExpressionStatement)$4).Expression, (Expression)$5, ((Statement)$7).ToBlock ()); + $$ = new ForStatement((Statement)$3, $4 is ExpressionStatement es4b ? es4b.Expression : null, (Expression)$5, ((Statement)$7).ToBlock ()); } | FOR '(' declaration expression_statement ')' statement { - $$ = new ForStatement((Statement)$3, ((ExpressionStatement)$4).Expression, ((Statement)$6).ToBlock ()); + $$ = new ForStatement((Statement)$3, $4 is ExpressionStatement es4c ? es4c.Expression : null, ((Statement)$6).ToBlock ()); } | FOR '(' declaration expression_statement expression ')' statement { - $$ = new ForStatement((Statement)$3, ((ExpressionStatement)$4).Expression, (Expression)$5, ((Statement)$7).ToBlock ()); + $$ = new ForStatement((Statement)$3, $4 is ExpressionStatement es4d ? es4d.Expression : null, (Expression)$5, ((Statement)$7).ToBlock ()); } ; diff --git a/CLanguage/Syntax/ForStatement.cs b/CLanguage/Syntax/ForStatement.cs index e23cb00..4f6a70f 100644 --- a/CLanguage/Syntax/ForStatement.cs +++ b/CLanguage/Syntax/ForStatement.cs @@ -11,11 +11,11 @@ namespace CLanguage.Syntax public class ForStatement : Statement { public Block InitBlock { get; private set; } - public Expression ContinueExpression { get; private set; } + public Expression? ContinueExpression { get; private set; } public Expression? NextExpression { get; private set; } public Block LoopBody { get; private set; } - public ForStatement (Statement initStatement, Expression continueExpr, Block body) + public ForStatement (Statement initStatement, Expression? continueExpr, Block body) { InitBlock = new Block (VariableScope.Local); if (initStatement != null) { @@ -25,7 +25,7 @@ public ForStatement (Statement initStatement, Expression continueExpr, Block bod LoopBody = body; } - public ForStatement (Statement initStatement, Expression continueExpr, Expression nextExpr, Block body) + public ForStatement (Statement initStatement, Expression? continueExpr, Expression nextExpr, Block body) { InitBlock = new Block (VariableScope.Local); if (initStatement != null) { @@ -63,9 +63,11 @@ protected override void DoEmit (EmitContext initialContext) // var conditionLabel = ec.DefineLabel (); ec.EmitLabel (conditionLabel); - ContinueExpression.Emit (ec); - ec.EmitCastToBoolean (ContinueExpression.GetEvaluatedCType (ec)); - ec.Emit (OpCode.BranchIfFalse, endLabel); + if (ContinueExpression != null) { + ContinueExpression.Emit (ec); + ec.EmitCastToBoolean (ContinueExpression.GetEvaluatedCType (ec)); + ec.Emit (OpCode.BranchIfFalse, endLabel); + } // // Fall through to the loop body diff --git a/CLanguageTests/InterpreterTests.cs b/CLanguageTests/InterpreterTests.cs index 4257505..78ef535 100644 --- a/CLanguageTests/InterpreterTests.cs +++ b/CLanguageTests/InterpreterTests.cs @@ -246,6 +246,58 @@ void main () { }"); } + [TestMethod] + public void ForLoopInfinite () + { + Run (@" +void main () { + int i = 0; + for (;;) { + i++; + if (i >= 10) break; + } + assertAreEqual (10, i); +}"); + } + + [TestMethod] + public void ForLoopEmptyCondition () + { + Run (@" +void main () { + int i; + for (i = 0; ; i++) { + if (i >= 5) break; + } + assertAreEqual (5, i); +}"); + } + + [TestMethod] + public void ForLoopEmptyIncrement () + { + Run (@" +void main () { + int i; + for (i = 0; i < 10;) { + i++; + } + assertAreEqual (10, i); +}"); + } + + [TestMethod] + public void ForLoopEmptyInit () + { + Run (@" +void main () { + int i = 0; + for (; i < 5; i++) { + } + assertAreEqual (5, i); +}"); + } + [TestMethod] public void WhileLoopWithBreak () { From 43fd50f0085a8cca09e3a83801a8ff135d06da8c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 31 Mar 2026 20:33:35 +0000 Subject: [PATCH 3/3] Simplify null checks to use 'as' with null-conditional operator Use ($4 as ExpressionStatement)?.Expression instead of pattern matching. Agent-Logs-Url: https://github.com/praeclarum/CLanguage/sessions/3f96ebb4-83a6-41af-bf99-d72fe14ad18f Co-authored-by: praeclarum <323548+praeclarum@users.noreply.github.com> --- CLanguage/Parser/CParser.cs | 8 ++++---- CLanguage/Parser/CParser.jay | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/CLanguage/Parser/CParser.cs b/CLanguage/Parser/CParser.cs index 602da46..579bb83 100644 --- a/CLanguage/Parser/CParser.cs +++ b/CLanguage/Parser/CParser.cs @@ -1825,25 +1825,25 @@ internal Object yyparse (yyParser.yyInput yyLex) case 292: #line 1115 "CParser.jay" { - yyVal = new ForStatement((Statement)yyVals[-3+yyTop], yyVals[-2+yyTop] is ExpressionStatement es4a ? es4a.Expression : null, ((Statement)yyVals[0+yyTop]).ToBlock ()); + yyVal = new ForStatement((Statement)yyVals[-3+yyTop], (yyVals[-2+yyTop] as ExpressionStatement)?.Expression, ((Statement)yyVals[0+yyTop]).ToBlock ()); } break; case 293: #line 1119 "CParser.jay" { - yyVal = new ForStatement((Statement)yyVals[-4+yyTop], yyVals[-3+yyTop] is ExpressionStatement es4b ? es4b.Expression : null, (Expression)yyVals[-2+yyTop], ((Statement)yyVals[0+yyTop]).ToBlock ()); + yyVal = new ForStatement((Statement)yyVals[-4+yyTop], (yyVals[-3+yyTop] as ExpressionStatement)?.Expression, (Expression)yyVals[-2+yyTop], ((Statement)yyVals[0+yyTop]).ToBlock ()); } break; case 294: #line 1123 "CParser.jay" { - yyVal = new ForStatement((Statement)yyVals[-3+yyTop], yyVals[-2+yyTop] is ExpressionStatement es4c ? es4c.Expression : null, ((Statement)yyVals[0+yyTop]).ToBlock ()); + yyVal = new ForStatement((Statement)yyVals[-3+yyTop], (yyVals[-2+yyTop] as ExpressionStatement)?.Expression, ((Statement)yyVals[0+yyTop]).ToBlock ()); } break; case 295: #line 1127 "CParser.jay" { - yyVal = new ForStatement((Statement)yyVals[-4+yyTop], yyVals[-3+yyTop] is ExpressionStatement es4d ? es4d.Expression : null, (Expression)yyVals[-2+yyTop], ((Statement)yyVals[0+yyTop]).ToBlock ()); + yyVal = new ForStatement((Statement)yyVals[-4+yyTop], (yyVals[-3+yyTop] as ExpressionStatement)?.Expression, (Expression)yyVals[-2+yyTop], ((Statement)yyVals[0+yyTop]).ToBlock ()); } break; case 297: diff --git a/CLanguage/Parser/CParser.jay b/CLanguage/Parser/CParser.jay index 2949f22..4b2ec44 100644 --- a/CLanguage/Parser/CParser.jay +++ b/CLanguage/Parser/CParser.jay @@ -1111,19 +1111,19 @@ iteration_statement } | FOR '(' expression_statement expression_statement ')' statement { - $$ = new ForStatement((Statement)$3, $4 is ExpressionStatement es4a ? es4a.Expression : null, ((Statement)$6).ToBlock ()); + $$ = new ForStatement((Statement)$3, ($4 as ExpressionStatement)?.Expression, ((Statement)$6).ToBlock ()); } | FOR '(' expression_statement expression_statement expression ')' statement { - $$ = new ForStatement((Statement)$3, $4 is ExpressionStatement es4b ? es4b.Expression : null, (Expression)$5, ((Statement)$7).ToBlock ()); + $$ = new ForStatement((Statement)$3, ($4 as ExpressionStatement)?.Expression, (Expression)$5, ((Statement)$7).ToBlock ()); } | FOR '(' declaration expression_statement ')' statement { - $$ = new ForStatement((Statement)$3, $4 is ExpressionStatement es4c ? es4c.Expression : null, ((Statement)$6).ToBlock ()); + $$ = new ForStatement((Statement)$3, ($4 as ExpressionStatement)?.Expression, ((Statement)$6).ToBlock ()); } | FOR '(' declaration expression_statement expression ')' statement { - $$ = new ForStatement((Statement)$3, $4 is ExpressionStatement es4d ? es4d.Expression : null, (Expression)$5, ((Statement)$7).ToBlock ()); + $$ = new ForStatement((Statement)$3, ($4 as ExpressionStatement)?.Expression, (Expression)$5, ((Statement)$7).ToBlock ()); } ;