Skip to content

Conversation

@sakina1303
Copy link

@sakina1303 sakina1303 commented Oct 18, 2025

This pull request introduces the debugging functionality into the KidCode project by integrating an ExecutionContext for managing program execution state, setting breakpoints, and controlling program flow (pause, resume, step, terminate).

Key Changes:

Added ExecutionContext class in the kidcode-web module to handle debugger state and controls.
Implemented DebuggerTest class to demonstrate breakpoint handling and debugger control flow.
Created DebuggerController REST API endpoints (/pause, /resume, /step, /terminate) to allow external control of program execution through HTTP calls.
Updated kidcode-web/pom.xml to include dependency on kidcode-core and Spring Boot starter web.
Fixed package import and type mismatches between core and web modules to ensure proper interaction.
Verified full build success and ability to run Spring Boot web application serving debugger endpoints.
How to Test:

Run the DebuggerTest main method to verify breakpoint pause and resume functionality.
Start the web application using mvn -pl kidcode-web spring-boot:run.
Use HTTP POST requests to the /debugger endpoints to pause, resume, step, or terminate execution.
Confirm expected behavior via logs and program state changes.
Notes:
This lays the groundwork for adding a full debugger UI or integration with IDEs.
Future work may include enhanced breakpoint management and state inspection APIs.

Screenshot:
Uploading Screenshot 2025-10-14 at 2.23.12 PM.png…

Fixes #16

Summary by CodeRabbit
New Features

Interactive debugging: pause, resume, step through, and terminate program execution.
Breakpoint support by line number to halt execution at specific points.
HTTP endpoints to control debugging actions remotely.
Improvements

Clearer runtime feedback with line-aware statements for easier troubleshooting.
Smoother execution control flow to better support pausing and stepping during runs.

Summary by CodeRabbit

  • New Features

    • Remote debugger endpoints to pause, resume, step, and terminate execution
    • Breakpoint support that stops execution at specified source lines
    • Stepping mode pauses after each statement for interactive inspection
  • Bug Fixes / Improvements

    • Improved source-line tracking for more accurate breakpoints
    • Parser more resilient to malformed input to avoid hanging
    • Execution can be paused/resumed safely across threads
  • Tests

    • Added a small debug-run utility demonstrating pause/step/resume flow

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 18, 2025

Walkthrough

Introduces a step-by-step debugger: adds ExecutionContext lifecycle, LocatedStatement for source line tracking, parser changes to emit locations, Evaluator breakpoint- and pause-aware execution with stepping, KidCodeEngine wiring to the context, REST debugger endpoints, and a local DebuggerTest utility.

Changes

Cohort / File(s) Summary
Execution control & engine wiring
kidcode-core/src/main/java/com/kidcode/core/evaluator/ExecutionContext.java, kidcode-core/src/main/java/com/kidcode/core/KidCodeEngine.java
New ExecutionContext with states RUNNING/PAUSED/STEPPING/TERMINATED and synchronized pause/step/resume/terminate/waitIfPaused; KidCodeEngine now creates/passes the context, combines context.isTerminated() with existing stop signal, and passes breakpoints to Evaluator.
Statement location tracking
kidcode-core/src/main/java/com/kidcode/core/ast/LocatedStatement.java, kidcode-core/src/main/java/com/kidcode/core/parser/Parser.java
Added LocatedStatement wrapper holding an inner Statement and source line number; Parser.parseStatement now wraps parsed statements in LocatedStatement and includes token-advancement and error-handling tweaks.
Evaluator debugging integration
kidcode-core/src/main/java/com/kidcode/core/evaluator/Evaluator.java
Evaluator constructor updated to accept (Supplier<Boolean> stopSignal, ExecutionContext context, Set<Integer> breakpoints); evaluator unwraps LocatedStatement to check line numbers, pauses when encountering breakpoints or when context is paused, supports stepping (pause after one statement), and returns unmodifiable events.
Web controller & test utility
kidcode-web/src/main/java/com/kidcode/web/controller/DebuggerController.java, kidcode-web/src/main/java/com/kidcode/web/DebuggerTest.java
New REST controller exposing /debugger endpoints: POST /pause, /resume, /step, /terminate that call ExecutionContext transitions; added DebuggerTest demonstrating evaluator run, breakpoint setup, pause/step/resume flow.

Sequence Diagram(s)

sequenceDiagram
    participant UI as Web UI
    participant DC as DebuggerController
    participant EC as ExecutionContext
    participant Eval as Evaluator
    participant Stmt as LocatedStatement

    Note over Eval,Stmt: Evaluator iterates statements (LocatedStatement)
    Eval->>Stmt: inspect lineNumber
    alt lineNumber in breakpoints
        Eval->>EC: set state PAUSED (via context) / notify UI
    end
    Eval->>EC: call waitIfPaused() (blocks while PAUSED)
    alt EC state = STEPPING
        Note right of EC: after single execute, context moves to PAUSED
    end

    UI->>DC: POST /debugger/pause
    DC->>EC: pause()

    UI->>DC: POST /debugger/step
    DC->>EC: step() -> notify() (unblocks one statement)

    UI->>DC: POST /debugger/resume
    DC->>EC: resume() -> notifyAll() (continue execution)

    UI->>DC: POST /debugger/terminate
    DC->>EC: terminate() -> notifyAll()
    Eval->>Eval: stop when EC.isTerminated() or stopSignal true
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐇 I hop through lines with tiny paws,

I pause at carrots drawn as clause.
One nudge, one step, then on I zoom,
Resume the dance — back to the room.
Debugger carrots — tidy, calm applause.

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Linked Issues Check ⚠️ Warning The PR implements most of the core architectural requirements from issue #16 but falls short of complete compliance. Objectives 1-3 are well-addressed: ExecutionContext with the required states (RUNNING, PAUSED, STEPPING, TERMINATED) is introduced [#16], the Evaluator is modified to check execution context before executing statements and pause at breakpoints, and breakpoint management is implemented by passing a Set to the Evaluator. However, objective 4 (variable inspection via Environment method dump and API endpoint) is not implemented—the PR description explicitly states this is "groundwork for debugger UI" with "future work may add enhanced breakpoint management and state inspection APIs." Additionally, objective 5 (UI controls) is only partially met with REST endpoints for pause/resume/step/terminate in DebuggerController, but lacks integration with the actual execution context and no indication of how this controller receives the active ExecutionContext instance. To achieve full compliance with issue #16, implement the missing variable inspection feature by adding an Environment method (e.g., dumpVariables()) to expose current-scope key-value pairs and an API endpoint in DebuggerController to fetch this state. Additionally, clarify and complete the integration between the core Evaluator's ExecutionContext and the web module's DebuggerController—ensure they share the same ExecutionContext instance and properly synchronize state across module boundaries. Consider adding integration tests demonstrating the complete flow from breakpoint hit through pause, inspection, resume, and termination.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The PR title "Added Debugger Support with ExecutionContext and Web API Integration and implemented bot's suggestions with synced main" accurately captures the main additions in the changeset. The title references the core components introduced: ExecutionContext for state management, debugger support through breakpoints and stepping, and Web API integration via DebuggerController. While the title is verbose and could be more concise, it successfully communicates the primary changes and is specific enough that a reviewer scanning the history would understand the scope of the feature addition.
Out of Scope Changes Check ✅ Passed All code changes in this PR are directly related to implementing the debugger feature described in issue #16. The LocatedStatement wrapper, ExecutionContext state management, Parser modifications to track line numbers, Evaluator integration of breakpoints and stepping, KidCodeEngine coordination of these components, and the DebuggerController REST API all serve the stated objective of adding debugger support. The DebuggerTest class is included as a utility to demonstrate debugger functionality. No changes appear to be unrelated to the debugger feature requirements or introduce functionality outside the stated scope of this PR.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Comment @coderabbitai help to get the list of available commands and usage tips.

@sakina1303
Copy link
Author

@Sansi-28 Hi, I’ve made the necessary changes as suggested. Kindly have a look.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (7)
kidcode-core/src/main/java/com/kidcode/core/ast/LocatedStatement.java (1)

7-10: Validate constructor inputs and make wrapper final.

Guard against null and invalid line numbers; declare the wrapper final for immutability.

Apply:

+import java.util.Objects;
 
-public class LocatedStatement implements Statement {
+public final class LocatedStatement implements Statement {
@@
-    public LocatedStatement(Statement inner, int lineNumber) {
-        this.inner = inner;
-        this.lineNumber = lineNumber;
-    }
+    public LocatedStatement(Statement inner, int lineNumber) {
+        this.inner = Objects.requireNonNull(inner, "inner");
+        if (lineNumber < 1) throw new IllegalArgumentException("lineNumber must be >= 1");
+        this.lineNumber = lineNumber;
+    }

Optionally add a concise toString() for better logs while debugging.

kidcode-web/src/main/java/com/kidcode/web/DebuggerTest.java (1)

15-48: Avoid race; wait until actually paused. Move to test sources.

The print/sleep assumes the VM hit the breakpoint; it may resume before Line 41. Poll for PAUSED (or add isPaused/getState), and place this utility under src/test/java to avoid shipping it.

Example minimal changes:

-        System.out.println("Program paused at breakpoint. Waiting 2 seconds...");
-        Thread.sleep(2000);
+        // Wait until paused at breakpoint (avoid race)
+        while (!contextIsPaused(context)) { Thread.sleep(20); }
+        System.out.println("Paused. Waiting 2 seconds...");
+        Thread.sleep(2000);

Add helper (or add ExecutionContext#isPaused()):

private static boolean contextIsPaused(ExecutionContext ctx) {
    try {
        var m = ExecutionContext.class.getDeclaredMethod("isStepping"); // placeholder; replace with isPaused()
        return false; // implement once isPaused() exists
    } catch (Exception e) { return false; }
}

Prefer: add public synchronized boolean isPaused() to ExecutionContext and use it here.

kidcode-core/src/main/java/com/kidcode/core/KidCodeEngine.java (1)

12-14: Accept breakpoints from caller.

To support UI‑provided breakpoints (issue #16), overload/extend execute to accept a Set and pass it through.

Example:

-    public List<ExecutionEvent> execute(String sourceCode) {
+    public List<ExecutionEvent> execute(String sourceCode, Set<Integer> breakpoints) {
kidcode-core/src/main/java/com/kidcode/core/evaluator/Evaluator.java (2)

61-67: Prevent repeated timeout errors and stop promptly.

When the instruction limit is exceeded, an ErrorEvent may be added for every subsequent statement (esp. inside loops). Emit once and terminate execution to short‑circuit nested flows.

-        if (stopSignal.get() || ++instructionCount > INSTRUCTION_LIMIT) {
-            if (instructionCount > INSTRUCTION_LIMIT) {
-                events.add(new ExecutionEvent.ErrorEvent("Execution timed out! Possible infinite loop."));
-            }
-            return;
-        }
+        if (stopSignal.get() || ++instructionCount > INSTRUCTION_LIMIT) {
+            if (instructionCount > INSTRUCTION_LIMIT && !timedOut) {
+                timedOut = true;
+                events.add(new ExecutionEvent.ErrorEvent("Execution timed out! Possible infinite loop."));
+                // Optional: notify controller/engine to stop
+                executionContext.terminate();
+            }
+            return;
+        }

Add field:

 private int instructionCount = 0;
+private boolean timedOut = false;

Reset in evaluate():

 instructionCount = 0;
+timedOut = false;

53-60: Expose pause metadata (optional).

Consider emitting an event when pausing at a breakpoint (with lineNumber) so UIs can highlight current line.

kidcode-core/src/main/java/com/kidcode/core/evaluator/ExecutionContext.java (2)

4-4: Remove unused import.

AtomicBoolean is imported but never used in the class.

Apply this diff:

-import java.util.concurrent.atomic.AtomicBoolean;
-

15-15: Consider removing redundant volatile modifier.

The state field is marked volatile, but all methods that access it are synchronized, which already provides memory visibility guarantees. The volatile modifier is redundant and can be removed for cleaner code.

Apply this diff:

-    private volatile State state = State.RUNNING;
+    private State state = State.RUNNING;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b6ecb77 and 2b48e09.

📒 Files selected for processing (7)
  • kidcode-core/src/main/java/com/kidcode/core/KidCodeEngine.java (2 hunks)
  • kidcode-core/src/main/java/com/kidcode/core/ast/LocatedStatement.java (1 hunks)
  • kidcode-core/src/main/java/com/kidcode/core/evaluator/Evaluator.java (7 hunks)
  • kidcode-core/src/main/java/com/kidcode/core/evaluator/ExecutionContext.java (1 hunks)
  • kidcode-core/src/main/java/com/kidcode/core/parser/Parser.java (2 hunks)
  • kidcode-web/src/main/java/com/kidcode/web/DebuggerTest.java (1 hunks)
  • kidcode-web/src/main/java/com/kidcode/web/controller/DebuggerController.java (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
kidcode-web/src/main/java/com/kidcode/web/DebuggerTest.java (3)
kidcode-core/src/main/java/com/kidcode/core/evaluator/Evaluator.java (1)
  • Evaluator (12-263)
kidcode-core/src/main/java/com/kidcode/core/evaluator/Environment.java (1)
  • Environment (8-94)
kidcode-core/src/main/java/com/kidcode/core/evaluator/ExecutionContext.java (1)
  • ExecutionContext (6-53)
kidcode-core/src/main/java/com/kidcode/core/evaluator/Evaluator.java (1)
kidcode-core/src/main/java/com/kidcode/core/evaluator/ExecutionContext.java (1)
  • ExecutionContext (6-53)
kidcode-core/src/main/java/com/kidcode/core/KidCodeEngine.java (2)
kidcode-core/src/main/java/com/kidcode/core/evaluator/ExecutionContext.java (1)
  • ExecutionContext (6-53)
kidcode-core/src/main/java/com/kidcode/core/evaluator/Environment.java (1)
  • Environment (8-94)
kidcode-web/src/main/java/com/kidcode/web/controller/DebuggerController.java (1)
kidcode-core/src/main/java/com/kidcode/core/evaluator/ExecutionContext.java (1)
  • ExecutionContext (6-53)
🔇 Additional comments (6)
kidcode-core/src/main/java/com/kidcode/core/evaluator/Evaluator.java (1)

117-118: Good change: error events for SayStatement failures.

Using ErrorEvent for evaluation failures improves clarity for UIs.

kidcode-web/src/main/java/com/kidcode/web/controller/DebuggerController.java (1)

3-7: Jakarta/Boot 3 compatibility looks fine.

Using org.springframework.web.bind.annotation is correct for Boot 3/Jakarta. No javax.* usage.

kidcode-core/src/main/java/com/kidcode/core/parser/Parser.java (2)

70-101: LGTM! Well-designed safeguard against parser stalls.

The refactored parseStatement() correctly captures line numbers and wraps results in LocatedStatement. The safeguard at lines 92-98 is particularly well-implemented: it ensures the parser advances at least one token when a parse method returns null without consuming tokens, preventing infinite loops. The switch expression cleanly dispatches to appropriate parsers, and the error handling in the default case properly advances the token stream.


136-137: Good catch on error recovery.

Adding advanceToNextStatement() in the error case prevents the parser from getting stuck when the expected = token is missing. This aligns with the overall token advancement refactoring and improves error recovery.

kidcode-core/src/main/java/com/kidcode/core/evaluator/ExecutionContext.java (2)

36-44: Correct wait/notify pattern implementation.

The waitIfPaused() method correctly implements the standard wait pattern:

  • Uses a while loop (not if) to guard against spurious wakeups
  • Properly handles InterruptedException by re-interrupting the current thread
  • Works correctly with the notify/notifyAll calls in step(), resume(), and terminate()

17-34: Thread-safe state transitions with proper coordination.

The state transition methods (pause(), step(), resume(), terminate()) are correctly synchronized and use notifyAll() appropriately to wake waiting threads. The use of notifyAll() rather than notify() is the right choice here to ensure all waiting threads are woken when state changes.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
kidcode-core/src/main/java/com/kidcode/core/evaluator/Evaluator.java (2)

58-66: Breakpoint pause flow looks good; add a cheap fast‑path.

Minor: skip pause() if already paused to avoid redundant state writes (optional).

-        if (lineNumber != -1 && breakpoints.contains(lineNumber)) {
-            executionContext.pause();
-        }
+        if (lineNumber != -1 && breakpoints.contains(lineNumber) && !executionContext.isStepping()) {
+            // If stepping, we'll pause right after executing the statement.
+            executionContext.pause();
+        }

120-125: Standardize error event types for consistency.

Here SayStatement errors emit ErrorEvent, but other error paths (e.g., Line 141 in IfStatement) still emit SayEvent. Unify to ErrorEvent so the UI can style/handle errors consistently.

Example minimal fix:

-            if (isError(cond)) {
-                events.add(new ExecutionEvent.SayEvent((String) cond));
+            if (isError(cond)) {
+                events.add(new ExecutionEvent.ErrorEvent((String) cond));
                 return;
             }

Optionally, adopt a single helper to translate any "Error:" string into ErrorEvent at the call sites.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2b48e09 and 196a4d5.

📒 Files selected for processing (2)
  • kidcode-core/src/main/java/com/kidcode/core/evaluator/Evaluator.java (7 hunks)
  • kidcode-core/src/main/java/com/kidcode/core/parser/Parser.java (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • kidcode-core/src/main/java/com/kidcode/core/parser/Parser.java
🧰 Additional context used
🧬 Code graph analysis (1)
kidcode-core/src/main/java/com/kidcode/core/evaluator/Evaluator.java (1)
kidcode-core/src/main/java/com/kidcode/core/evaluator/ExecutionContext.java (1)
  • ExecutionContext (6-53)
🔇 Additional comments (6)
kidcode-core/src/main/java/com/kidcode/core/evaluator/Evaluator.java (6)

50-57: LGTM: clean unwrap of LocatedStatement for line mapping.

Pattern matching keeps code concise and prepares for breakpoint checks.


162-165: Stepping semantics implemented correctly (step → execute one → pause).

This aligns with the described UX.


35-37: LGTM: initial Clear + Move event initialization is consistent with prior state emission.


201-202: LGTM: explicit 'variable not found' error improves debuggability.


246-251: LGTM: strong type and bounds checks for list indexing with clear messages.


53-56: Java compiler configuration correctly set to Java 17 across all modules.

The parent POM (pom.xml) establishes <maven.compiler.source>17</maven.compiler.source> and <maven.compiler.target>17</maven.compiler.target>, which all child modules (kidcode-core, kidcode-desktop, kidcode-web) inherit. The instanceof pattern variable syntax used in Evaluator.java (lines 53-56) is fully supported by Java 16+ and will compile correctly under Java 17. The Spring Boot 3.0.0 dependency in kidcode-web also confirms Java 17+ alignment.

@Sansi-28
Copy link
Owner

@Sansi-28 Hi, I’ve made the necessary changes as suggested. Kindly have a look.

@sakina1303 make the changes suggested by coderabbitsai and we are good to go

@sakina1303 sakina1303 force-pushed the feature/debugger-from-main branch from 196a4d5 to ae163b9 Compare October 18, 2025 14:10
@sakina1303
Copy link
Author

@Sansi-28 Hi, I’ve made the necessary changes as suggested. Kindly have a look.

@sakina1303 make the changes suggested by coderabbitsai and we are good to go

done

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
kidcode-core/src/main/java/com/kidcode/core/parser/Parser.java (1)

65-94: parseStatement is malformed and won’t compile; wrapper logic incomplete.

  • Stray closing brace after switch; dangling block referencing undeclared variables: inner, startPos, line.
  • Early returns in switch prevent wrapping in LocatedStatement.

Fix by restructuring parseStatement to capture line/start, select inner via switch, recover on null, then wrap:

-    private Statement parseStatement() {
-        switch (currentToken().type()) {
-            case MOVE: return parseMoveStatement();
-            case TURN: return parseTurnStatement();
-            case SAY: return parseSayStatement();
-            case REPEAT: return parseRepeatStatement();
-            case SET: return parseSetStatement();
-            case IF: return parseIfStatement();
-            case PEN: return parsePenStatement();
-            case COLOR: return parseSetColorStatement();
-            case DEFINE: return parseFunctionDefinitionStatement();
-            case IDENTIFIER: return parseFunctionCallStatement();
-            default:
-                errors.add("Error line " + currentToken().lineNumber() + ": Invalid start of a statement: '" + currentToken().literal() + "'");
-                advanceToNextStatement();
-                return null;
-        }
-        };
-
-    if (inner == null) {
-        // Some parse... methods return null on error without consuming tokens.
-        // Advance at least one token to avoid infinite loops.
-        if (position == startPos) {
-            advanceToNextStatement();
-        }
-        return null;
-    }
-    return new LocatedStatement(inner, line); // wrap it with line number
-}
+    private Statement parseStatement() {
+        final int line = currentToken().lineNumber();
+        final int startPos = position;
+        Statement inner = null;
+        switch (currentToken().type()) {
+            case MOVE: inner = parseMoveStatement(); break;
+            case TURN: inner = parseTurnStatement(); break;
+            case SAY: inner = parseSayStatement(); break;
+            case REPEAT: inner = parseRepeatStatement(); break;
+            case SET: inner = parseSetStatement(); break;
+            case IF: inner = parseIfStatement(); break;
+            case PEN: inner = parsePenStatement(); break;
+            case COLOR: inner = parseSetColorStatement(); break;
+            case DEFINE: inner = parseFunctionDefinitionStatement(); break;
+            case IDENTIFIER: inner = parseFunctionCallStatement(); break;
+            default:
+                errors.add("Error line " + line + ": Invalid start of a statement: '" + currentToken().literal() + "'");
+                advanceToNextStatement();
+                return null;
+        }
+        if (inner == null) {
+            if (position == startPos) {
+                advanceToNextStatement();
+            }
+            return null;
+        }
+        return new LocatedStatement(inner, line);
+    }
♻️ Duplicate comments (3)
kidcode-core/src/main/java/com/kidcode/core/KidCodeEngine.java (1)

39-46: Compile breakers and isolated context; inject shared ExecutionContext.

  • stopSignal declared twice (Line 39 and Line 42).
  • Local new ExecutionContext() isolates from web controller; HTTP controls won’t affect execution.
  • environment is undefined at return.

Apply minimal fix + constructor injection:

-        Supplier<Boolean> stopSignal = () -> executionStopped;
-     ExecutionContext context = new ExecutionContext();
-      Set<Integer> breakpoints = new HashSet<>();  // empty set for now, add breakpoints as needed
-        Supplier<Boolean> stopSignal = () -> executionStopped || context.isTerminated();
+        Set<Integer> breakpoints = new HashSet<>();
+        Supplier<Boolean> stopSignal = () -> executionStopped || context.isTerminated();
 
-        Evaluator evaluator = new Evaluator(stopSignal, context, breakpoints);
-        return evaluator.evaluate(program, environment);
+        Evaluator evaluator = new Evaluator(stopSignal, context, breakpoints);
+        Environment env = new Environment();
+        return evaluator.evaluate(program, env);

And refactor class to receive shared context (bean-injected):

 public class KidCodeEngine {
-    private volatile boolean executionStopped = false;
+    private volatile boolean executionStopped = false;
+    private final ExecutionContext context;
+
+    public KidCodeEngine(ExecutionContext context) {
+        this.context = context;
+    }

Provide the bean in web config to share the same instance with DebuggerController.

kidcode-core/src/main/java/com/kidcode/core/evaluator/Evaluator.java (2)

24-28: Constructor lacks null-safety and defensive copy for breakpoints.

Avoid NPE and external mutation:

+import java.util.Objects;
@@
-    public Evaluator(Supplier<Boolean> stopSignal, ExecutionContext context, Set<Integer> breakpoints) {
-        this.stopSignal = stopSignal;
-        this.executionContext = context;
-        this.breakpoints = breakpoints;
+    public Evaluator(Supplier<Boolean> stopSignal, ExecutionContext context, Set<Integer> breakpoints) {
+        this.stopSignal = Objects.requireNonNull(stopSignal, "stopSignal");
+        this.executionContext = Objects.requireNonNull(context, "executionContext");
+        this.breakpoints = (breakpoints == null) ? Set.of() : Set.copyOf(breakpoints);
     }

67-76: Terminate isn’t fully honored; add checks after waits and in loops.

Ensure immediate stop when terminated:

@@
-        executionContext.waitIfPaused();
+        executionContext.waitIfPaused();
+        if (executionContext.isTerminated()) {
+            return;
+        }
@@
-        for (Statement statement : program) {
-            if (stopSignal.get()) break;
+        for (Statement statement : program) {
+            if (stopSignal.get() || executionContext.isTerminated()) break;
             evaluateStatement(statement, env);
+            if (executionContext.isTerminated()) break;
         }
@@
-            for (int i = 0; i < times; i++) {
-                if (stopSignal.get()) return;
+            for (int i = 0; i < times; i++) {
+                if (stopSignal.get() || executionContext.isTerminated()) return;
                 for (Statement bodyStatement : repeatStmt.body()) {
-                    evaluateStatement(bodyStatement, env);
+                    if (stopSignal.get() || executionContext.isTerminated()) return;
+                    evaluateStatement(bodyStatement, env);
+                    if (executionContext.isTerminated()) return;
                 }
             }
@@
-        for (Statement bodyStmt : func.body()) {
-            evaluateStatement(bodyStmt, localEnv);
-        }
+        for (Statement bodyStmt : func.body()) {
+            if (stopSignal.get() || executionContext.isTerminated()) return;
+            evaluateStatement(bodyStmt, localEnv);
+            if (executionContext.isTerminated()) return;
+        }

Also include executionContext.isTerminated() in the per-statement guard:

-        if (stopSignal.get() || ++instructionCount > INSTRUCTION_LIMIT) {
+        if (stopSignal.get() || executionContext.isTerminated() || ++instructionCount > INSTRUCTION_LIMIT) {

Also applies to: 38-43, 136-141, 300-303

🧹 Nitpick comments (3)
kidcode-core/src/main/java/com/kidcode/core/parser/Parser.java (1)

219-221: Double-advance semantics around end if.

The sequence “if (peek==IF) nextToken(); advanceToNextStatement();” effectively consumes both tokens of “end if”, but the comment says it consumes ‘if’. Consider clarifying the comment or explicitly consuming END then IF for readability. Behavior is fine.

kidcode-core/src/main/java/com/kidcode/core/evaluator/Evaluator.java (1)

58-66: Duplicate breakpoint pause block.

Lines 58-61 and 63-65 are identical. Remove one.

-        // Pause if breakpoint
-        if (lineNumber != -1 && breakpoints.contains(lineNumber)) {
-            executionContext.pause();
-        }
- // Pause if breakpoint
-        if (lineNumber != -1 && breakpoints.contains(lineNumber)) {
-            executionContext.pause();
-        }
+        // Pause if breakpoint
+        if (lineNumber != -1 && breakpoints.contains(lineNumber)) {
+            executionContext.pause();
+        }
kidcode-core/src/main/java/com/kidcode/core/evaluator/ExecutionContext.java (1)

15-53: Add await/isPaused helpers to support external synchronization.

To make tests and web UI robust, expose non-blocking query and timed await:

@@
     public synchronized boolean isStepping() {
         return state == State.STEPPING;
     }
+
+    public synchronized boolean isPaused() {
+        return state == State.PAUSED;
+    }
+
+    public synchronized boolean awaitPaused(long timeoutMs) throws InterruptedException {
+        long end = System.currentTimeMillis() + timeoutMs;
+        while (state != State.PAUSED && state != State.TERMINATED) {
+            long waitFor = end - System.currentTimeMillis();
+            if (waitFor <= 0) break;
+            wait(waitFor);
+        }
+        return state == State.PAUSED;
+    }

Optional: remove unused AtomicBoolean import.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 196a4d5 and d7ac4e9.

📒 Files selected for processing (7)
  • kidcode-core/src/main/java/com/kidcode/core/KidCodeEngine.java (2 hunks)
  • kidcode-core/src/main/java/com/kidcode/core/ast/LocatedStatement.java (1 hunks)
  • kidcode-core/src/main/java/com/kidcode/core/evaluator/Evaluator.java (7 hunks)
  • kidcode-core/src/main/java/com/kidcode/core/evaluator/ExecutionContext.java (1 hunks)
  • kidcode-core/src/main/java/com/kidcode/core/parser/Parser.java (2 hunks)
  • kidcode-web/src/main/java/com/kidcode/web/DebuggerTest.java (1 hunks)
  • kidcode-web/src/main/java/com/kidcode/web/controller/DebuggerController.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • kidcode-core/src/main/java/com/kidcode/core/ast/LocatedStatement.java
  • kidcode-web/src/main/java/com/kidcode/web/controller/DebuggerController.java
🧰 Additional context used
🧬 Code graph analysis (3)
kidcode-core/src/main/java/com/kidcode/core/evaluator/Evaluator.java (1)
kidcode-web/src/main/resources/static/app.js (1)
  • events (193-193)
kidcode-core/src/main/java/com/kidcode/core/KidCodeEngine.java (2)
kidcode-core/src/main/java/com/kidcode/core/evaluator/ExecutionContext.java (1)
  • ExecutionContext (6-53)
kidcode-core/src/main/java/com/kidcode/core/evaluator/Environment.java (1)
  • Environment (8-94)
kidcode-web/src/main/java/com/kidcode/web/DebuggerTest.java (3)
kidcode-core/src/main/java/com/kidcode/core/evaluator/Evaluator.java (1)
  • Evaluator (12-304)
kidcode-core/src/main/java/com/kidcode/core/evaluator/Environment.java (1)
  • Environment (8-94)
kidcode-core/src/main/java/com/kidcode/core/evaluator/ExecutionContext.java (1)
  • ExecutionContext (6-53)
🔇 Additional comments (2)
kidcode-core/src/main/java/com/kidcode/core/parser/Parser.java (2)

126-127: Good recovery on missing '=' in set.

Advancing on error avoids infinite loops. LGTM.


223-234: parseBlock entry nextToken() changes contract; verify all callers.

parseBlock now consumes the starter/else token. Callers (if/define/repeat) appear aligned, but this is brittle. Consider documenting this contract in the method Javadoc to avoid future double-consume bugs.

@Sansi-28
Copy link
Owner

@sakina1303 Their is the last one.

@sakina1303 sakina1303 force-pushed the feature/debugger-from-main branch from d7ac4e9 to 6c8c2f6 Compare October 18, 2025 14:21
@sakina1303
Copy link
Author

@Sansi-28 done.

@Sansi-28
Copy link
Owner

Sansi-28 commented Oct 18, 2025

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 18, 2025

✅ Actions performed

Full review triggered.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 18, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
kidcode-core/src/main/java/com/kidcode/core/parser/Parser.java (1)

65-93: Critical syntax error: malformed parseStatement method.

The method has multiple compile-breaking issues:

  1. Line 82: Stray }; closing brace after the switch statement. Each case already returns directly, so this brace is unmatched.
  2. Lines 84-93: References undeclared variables inner, line, and startPos. These don't exist in the method scope.
  3. Missing wrapper logic: The intent to wrap statements in LocatedStatement is clear from the AI summary, but the implementation is incomplete.

The method won't compile as-is.

Fix by capturing the line number, storing the result, and wrapping properly:

 private Statement parseStatement() {
+    int line = currentToken().lineNumber();
+    int startPos = position;
+    Statement inner;
+    
     switch (currentToken().type()) {
-        case MOVE: return parseMoveStatement();
-        case TURN: return parseTurnStatement();
-        case SAY: return parseSayStatement();
-        case REPEAT: return parseRepeatStatement();
-        case SET: return parseSetStatement();
-        case IF: return parseIfStatement();
-        case PEN: return parsePenStatement();
-        case COLOR: return parseSetColorStatement();
-        case DEFINE: return parseFunctionDefinitionStatement();
-        case IDENTIFIER: return parseFunctionCallStatement();
+        case MOVE: inner = parseMoveStatement(); break;
+        case TURN: inner = parseTurnStatement(); break;
+        case SAY: inner = parseSayStatement(); break;
+        case REPEAT: inner = parseRepeatStatement(); break;
+        case SET: inner = parseSetStatement(); break;
+        case IF: inner = parseIfStatement(); break;
+        case PEN: inner = parsePenStatement(); break;
+        case COLOR: inner = parseSetColorStatement(); break;
+        case DEFINE: inner = parseFunctionDefinitionStatement(); break;
+        case IDENTIFIER: inner = parseFunctionCallStatement(); break;
         default:
             errors.add("Error line " + currentToken().lineNumber() + ": Invalid start of a statement: '" + currentToken().literal() + "'");
             advanceToNextStatement();
             return null;
-    }
-    };
+    }
 
     if (inner == null) {
-        // Some parse... methods return null on error without consuming tokens.
-        // Advance at least one token to avoid infinite loops.
         if (position == startPos) {
             advanceToNextStatement();
         }
         return null;
     }
-    return new LocatedStatement(inner, line); // wrap it with line number
+    return new LocatedStatement(inner, line);
 }
♻️ Duplicate comments (6)
kidcode-web/src/main/java/com/kidcode/web/controller/DebuggerController.java (2)

14-37: Add state endpoint and security hardening.

UI needs a way to poll debugger state. Additionally, these endpoints lack authentication—anyone can control execution.

Add state endpoint:

     @PostMapping("/terminate")
     public String terminate() {
         context.terminate();
         return "Terminated";
     }
+
+    @GetMapping("/state")
+    public String state() {
+        if (context.isTerminated()) return "TERMINATED";
+        if (context.isStepping()) return "STEPPING";
+        // Add isPaused() to ExecutionContext if needed, or return generic state
+        return "RUNNING";
+    }

For dev security, bind to localhost in application.properties:

server.address=127.0.0.1

Or add Spring Security for production use.


11-13: Missing Spring bean configuration for ExecutionContext.

Constructor requires ExecutionContext but no @Bean or @Component provides it. Application will fail to start with "No qualifying bean" error.

Create configuration:

// kidcode-web/src/main/java/com/kidcode/web/config/DebuggerConfig.java
package com.kidcode.web.config;

import com.kidcode.core.evaluator.ExecutionContext;
import com.kidcode.core.KidCodeEngine;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class DebuggerConfig {
    @Bean
    public ExecutionContext executionContext() {
        return new ExecutionContext();
    }
    
    @Bean
    public KidCodeEngine kidCodeEngine(ExecutionContext context) {
        return new KidCodeEngine(context);
    }
}
kidcode-core/src/main/java/com/kidcode/core/KidCodeEngine.java (1)

39-45: Critical: Duplicate declaration, undefined variable, and isolated ExecutionContext.

Three blocking issues remain unresolved from prior review:

  1. Line 39 & 42: stopSignal declared twice.
  2. Line 45: environment is undefined—no local or field declaration exists.
  3. Line 40: Creating a new ExecutionContext locally isolates it from DebuggerController's instance; HTTP debugger controls won't affect this execution.

Minimal fix:

-        Supplier<Boolean> stopSignal = () -> executionStopped;
-     ExecutionContext context = new ExecutionContext();
-      Set<Integer> breakpoints = new HashSet<>();  // empty set for now, add breakpoints as needed
-        Supplier<Boolean> stopSignal = () -> executionStopped || context.isTerminated();
+        ExecutionContext context = new ExecutionContext(); // TODO: inject shared instance
+        Set<Integer> breakpoints = new HashSet<>();
+        Supplier<Boolean> stopSignal = () -> executionStopped || context.isTerminated();
+        Environment environment = new Environment();
 
         Evaluator evaluator = new Evaluator(stopSignal, context, breakpoints);
         return evaluator.evaluate(program, environment);

Essential: Refactor to inject a shared ExecutionContext via constructor so HTTP controls work:

 public class KidCodeEngine {
     private volatile boolean executionStopped = false;
+    private final ExecutionContext context;
+
+    public KidCodeEngine(ExecutionContext context) {
+        this.context = context;
+    }
 
     public List<ExecutionEvent> execute(String sourceCode) {
         this.executionStopped = false;
         // ...
-        ExecutionContext context = new ExecutionContext();
+        // Use this.context

Provide the bean in web config:

// kidcode-web/src/main/java/com/kidcode/web/config/DebuggerConfig.java
@Configuration
public class DebuggerConfig {
    @Bean public ExecutionContext executionContext() { return new ExecutionContext(); }
    @Bean public KidCodeEngine engine(ExecutionContext ctx) { return new KidCodeEngine(ctx); }
}
kidcode-web/src/main/java/com/kidcode/web/DebuggerTest.java (1)

40-45: Race condition: assumes paused without confirmation.

Main thread prints "Program paused..." and sleeps without verifying the evaluator reached the breakpoint. The evaluator thread may not have hit line 2 yet, making the test nondeterministic.

Add an awaitPaused helper to ExecutionContext:

+    public synchronized boolean awaitPaused(long timeoutMs) throws InterruptedException {
+        long end = System.currentTimeMillis() + timeoutMs;
+        while (state != State.PAUSED && state != State.TERMINATED) {
+            long waitFor = end - System.currentTimeMillis();
+            if (waitFor <= 0) return false;
+            wait(waitFor);
+        }
+        return state == State.PAUSED;
+    }

Then in DebuggerTest:

-         System.out.println("Program paused at breakpoint. Waiting 2 seconds...");
-        Thread.sleep(2000);
+        if (context.awaitPaused(5_000)) {
+            System.out.println("Program paused at breakpoint. Waiting 2 seconds...");
+            Thread.sleep(2000);
+        } else {
+            System.out.println("Did not pause within timeout.");
+        }
kidcode-core/src/main/java/com/kidcode/core/evaluator/Evaluator.java (2)

24-28: Null-safety and defensive copy needed for constructor parameters.

Constructor stores inputs directly without validation. Null breakpoints will cause NPE at line 59, and external mutation of the set can alter behavior unexpectedly.

Apply validation and defensive copy:

+import java.util.Objects;
+
 public class Evaluator {
     // ...
     public Evaluator(Supplier<Boolean> stopSignal, ExecutionContext context, Set<Integer> breakpoints) {
-        this.stopSignal = stopSignal;
-        this.executionContext = context;
-        this.breakpoints = breakpoints;
+        this.stopSignal = Objects.requireNonNull(stopSignal, "stopSignal");
+        this.executionContext = Objects.requireNonNull(context, "executionContext");
+        this.breakpoints = (breakpoints == null) ? Set.of() : Set.copyOf(breakpoints);
     }

58-75: Terminate state not honored; evaluation continues after terminate().

Currently only stopSignal and instruction limit gate execution. If terminate() is called, evaluation proceeds. Add executionContext.isTerminated() checks after waitIfPaused(), in the per-statement guard, and in all loops.

Apply these checks:

         // Wait if paused
         executionContext.waitIfPaused();
+        // Stop immediately if terminated while waiting
+        if (executionContext.isTerminated()) {
+            return;
+        }
 
-        if (stopSignal.get() || ++instructionCount > INSTRUCTION_LIMIT) {
+        if (stopSignal.get() || executionContext.isTerminated() || ++instructionCount > INSTRUCTION_LIMIT) {
         if (instructionCount > INSTRUCTION_LIMIT) {
                 events.add(new ExecutionEvent.ErrorEvent("Execution timed out! Possible infinite loop."));
             }
             return;
         }

Also update the outer loop (line 38):

         for (Statement statement : program) {
-            if (stopSignal.get()) break;
+            if (stopSignal.get() || executionContext.isTerminated()) break;
             evaluateStatement(statement, env);
+            if (executionContext.isTerminated()) break;
         }

And inner loops (RepeatStatement line 136, function body line 300):

             for (int i = 0; i < times; i++) {
-                if (stopSignal.get()) return;
+                if (stopSignal.get() || executionContext.isTerminated()) return;
                 for (Statement bodyStatement : repeatStmt.body()) {
+                    if (stopSignal.get() || executionContext.isTerminated()) return;
                     evaluateStatement(bodyStatement, env);
+                    if (executionContext.isTerminated()) return;
                 }
             }
         for (Statement bodyStmt : func.body()) {
+            if (stopSignal.get() || executionContext.isTerminated()) return;
             evaluateStatement(bodyStmt, localEnv);
+            if (executionContext.isTerminated()) return;
         }
🧹 Nitpick comments (3)
kidcode-core/src/main/java/com/kidcode/core/ast/LocatedStatement.java (1)

1-24: Add null validation for inner statement.

The constructor accepts inner without validation. A null inner would cause NPE in tokenLiteral() (line 22) and getInner() (line 13).

Add validation:

+import java.util.Objects;
+
 public class LocatedStatement implements Statement {
     private final Statement inner;
     private final int lineNumber;
 
     public LocatedStatement(Statement inner, int lineNumber) {
-        this.inner = inner;
+        this.inner = Objects.requireNonNull(inner, "inner statement cannot be null");
         this.lineNumber = lineNumber;
     }
kidcode-core/src/main/java/com/kidcode/core/evaluator/ExecutionContext.java (2)

4-4: Unused import: AtomicBoolean.

AtomicBoolean is imported but never used. Remove it.

-import java.util.concurrent.atomic.AtomicBoolean;
-
 public class ExecutionContext {

36-44: Add timeout-based awaitPaused helper and isPaused query.

waitIfPaused() can block indefinitely if state never changes. A timeout-based helper would support safer testing and UI polling. Additionally, an isPaused() query method would enable the state endpoint in DebuggerController.

+    public synchronized boolean isPaused() {
+        return state == State.PAUSED;
+    }
+
+    public synchronized boolean awaitPaused(long timeoutMs) throws InterruptedException {
+        long end = System.currentTimeMillis() + timeoutMs;
+        while (state != State.PAUSED && state != State.TERMINATED) {
+            long waitFor = end - System.currentTimeMillis();
+            if (waitFor <= 0) return false;
+            wait(waitFor);
+        }
+        return state == State.PAUSED;
+    }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b6ecb77 and 35a6998.

📒 Files selected for processing (7)
  • kidcode-core/src/main/java/com/kidcode/core/KidCodeEngine.java (2 hunks)
  • kidcode-core/src/main/java/com/kidcode/core/ast/LocatedStatement.java (1 hunks)
  • kidcode-core/src/main/java/com/kidcode/core/evaluator/Evaluator.java (7 hunks)
  • kidcode-core/src/main/java/com/kidcode/core/evaluator/ExecutionContext.java (1 hunks)
  • kidcode-core/src/main/java/com/kidcode/core/parser/Parser.java (2 hunks)
  • kidcode-web/src/main/java/com/kidcode/web/DebuggerTest.java (1 hunks)
  • kidcode-web/src/main/java/com/kidcode/web/controller/DebuggerController.java (1 hunks)
🔇 Additional comments (3)
kidcode-core/src/main/java/com/kidcode/core/parser/Parser.java (3)

124-127: Good addition: prevents parser stall on missing '='.

Adding advanceToNextStatement() here ensures the parser doesn't loop infinitely when the = token is missing after a variable name.


219-219: Good fix: token advancement in parseIfStatement.

Ensures the token stream advances after processing the complete if statement.


223-234: No issues found — design is consistent across all call sites.

The review comment's concern about callers double-consuming tokens is unfounded. The code correctly implements the shifted responsibility: all four call sites (parseFunctionDefinitionStatement line 181, parseRepeatStatement line 194, and parseIfStatement lines 207 & 211) leave currentToken positioned such that parseBlock()'s nextToken() call at line 225 correctly consumes the next token without duplication. The ELSE case at line 211 is handled correctly—when currentToken is ELSE, parseBlock() consumes it as the "keyword that started the block." No caller performs redundant consumption.

Comment on lines +58 to +65
// Pause if breakpoint
if (lineNumber != -1 && breakpoints.contains(lineNumber)) {
executionContext.pause();
}
// Pause if breakpoint
if (lineNumber != -1 && breakpoints.contains(lineNumber)) {
executionContext.pause();
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Duplicate breakpoint-check code.

Lines 59-61 and 63-65 contain identical breakpoint pause logic. Remove the duplication.

-        // Pause if breakpoint
-        if (lineNumber != -1 && breakpoints.contains(lineNumber)) {
-            executionContext.pause();
-        }
  // Pause if breakpoint
         if (lineNumber != -1 && breakpoints.contains(lineNumber)) {
             executionContext.pause();
         }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Pause if breakpoint
if (lineNumber != -1 && breakpoints.contains(lineNumber)) {
executionContext.pause();
}
// Pause if breakpoint
if (lineNumber != -1 && breakpoints.contains(lineNumber)) {
executionContext.pause();
}
// Pause if breakpoint
if (lineNumber != -1 && breakpoints.contains(lineNumber)) {
executionContext.pause();
}
🤖 Prompt for AI Agents
In kidcode-core/src/main/java/com/kidcode/core/evaluator/Evaluator.java around
lines 58 to 65 there is duplicated breakpoint-check logic; remove the redundant
second block (lines 63–65) so only a single check remains, or consolidate both
into one check that calls executionContext.pause() when lineNumber != -1 and
breakpoints.contains(lineNumber); keep surrounding formatting and comments
intact.

@Sansi-28
Copy link
Owner

Sansi-28 commented Oct 18, 2025

Hey, @sakina1303 have you tried running the app in your local environment coz its not able to compile in mine?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature: Implement a Step-by-Step Debugger with Breakpoint Support

2 participants