debugger: evaluate as a snippet compiled like a REPL entry #39

Closed
opened 2026-10-05 21:07:13 +00:00 by orion · 11 comments
Collaborator

Context

Evaluate supports method calls by compiling the expression like a REPL entry (section 12).

Source

Acceptance criteria

  • Evaluate runs method calls
  • A session in tests/debug/

Dependencies

None known.

Moved from the planning lists of the docs; the design record stays the specification.

## Context Evaluate supports method calls by compiling the expression like a REPL entry (section 12). ## Source - [DEBUGGER.md section 20](https://git.torb.dev/torbscript/language/src/commit/393ef2608e4a60b8ff76d8b6b408951b822d887d/docs/design/DEBUGGER.md#L412) ## Acceptance criteria - [ ] Evaluate runs method calls - [ ] A session in `tests/debug/` ## Dependencies None known. _Moved from the planning lists of the docs; the design record stays the specification._
Author
Collaborator

[loop:torbscript] intake: in scope (author orion, label loop). Implements DEBUGGER.md section 20 / section 12 (evaluate compiles the expression like a REPL entry, so method calls work); the design record is the specification, no collision with CONCEPT "Open Questions". Shares the evaluate path with the conditional-breakpoint issue, so the two may need ordering; settled in refine. Touches the debugger and the REPL compile path, seed impact to be confirmed. next: refine (queued)

[loop:torbscript] intake: in scope (author orion, label loop). Implements DEBUGGER.md section 20 / section 12 (evaluate compiles the expression like a REPL entry, so method calls work); the design record is the specification, no collision with CONCEPT "Open Questions". Shares the evaluate path with the conditional-breakpoint issue, so the two may need ordering; settled in refine. Touches the debugger and the REPL compile path, seed impact to be confirmed. next: refine (queued)
Author
Collaborator

[loop:torbscript] refine: still queued behind the intake order. next: waiting (ready buffer full: #4, #163, #164, #223 are ready) until 2026-10-10T04:00:00Z

[loop:torbscript] refine: still queued behind the intake order. next: waiting (ready buffer full: #4, #163, #164, #223 are ready) until 2026-10-10T04:00:00Z
Author
Collaborator

[loop:torbscript] claim refine by session:loop-torbscript-20261022 until 2026-10-23T01:30:00Z

[loop:torbscript] claim refine by session:loop-torbscript-20261022 until 2026-10-23T01:30:00Z
Author
Collaborator

[loop:torbscript] decided by the loop: scope of this issue is the first slice of the snippet evaluator, one PR. Read from DEBUGGER.md sections 12, 15, 20 and REPL.md sections 4, 5, 7.

  • Fallback, not replacement. The restricted evaluator (compiler/src/debugger/evaluate.trb) stays the first path: hover, watch, logpoints and conditions keep their read-only, no-code-runs guarantee (section 7 and 12 rule it for them). Only a debug-console evaluate (context: repl) whose text parseWatch refuses goes to the snippet path. Why: the design record keeps the cheap path for hover and watch, and a hover that runs code would change a stopped program on a mouse move.
  • One expression, innermost frame. The text is one expression (calls of functions and methods allowed), evaluated in the frame evaluate names; deeper frames and statements/declarations are out of this slice (the follow-up is filed when this lands, just in time). Why: that is the case section 20 names ("method calls included") and it keeps the PR to one or two commits.
  • Bindings as parameters. As REPL.md section 4/5: the frame's Locals whose type can be written become the parameters of a synthesized replEntry-style function (const by value; a boxed value is lent, so a method that mutates it is visible, as in any native debugger). A Local whose type cannot be written is left out, and a snippet that names it answers <name> is not available in the snippet.
  • Sandbox. Runs like a REPL entry (REPL.md section 7): a panic or an interrupt ends the snippet and answers <error: why>; the debugged program stays stopped and intact. Output of the snippet (print) goes to the console as an output event, not into the debuggee's stdout.
  • Result. Shown as variables shows a value (showValue), with type and a variablesReference where it has children.
  • Names. No new protocol surface: same DAP evaluate, same response fields; a compile error of the snippet is the response message (success: false) with the checker's text relative to the snippet.
[loop:torbscript] decided by the loop: scope of this issue is the **first slice of the snippet evaluator**, one PR. Read from DEBUGGER.md sections 12, 15, 20 and REPL.md sections 4, 5, 7. - **Fallback, not replacement.** The restricted evaluator (`compiler/src/debugger/evaluate.trb`) stays the first path: hover, watch, logpoints and conditions keep their read-only, no-code-runs guarantee (section 7 and 12 rule it for them). Only a debug-console `evaluate` (`context: repl`) whose text `parseWatch` refuses goes to the snippet path. Why: the design record keeps the cheap path for hover and watch, and a hover that runs code would change a stopped program on a mouse move. - **One expression, innermost frame.** The text is one expression (calls of functions and methods allowed), evaluated in the frame `evaluate` names; deeper frames and statements/declarations are out of this slice (the follow-up is filed when this lands, just in time). Why: that is the case section 20 names ("method calls included") and it keeps the PR to one or two commits. - **Bindings as parameters.** As REPL.md section 4/5: the frame's Locals whose type can be written become the parameters of a synthesized `replEntry`-style function (`const` by value; a boxed value is lent, so a method that mutates it is visible, as in any native debugger). A Local whose type cannot be written is left out, and a snippet that names it answers `<name> is not available in the snippet`. - **Sandbox.** Runs like a REPL entry (REPL.md section 7): a panic or an interrupt ends the snippet and answers `<error: why>`; the debugged program stays stopped and intact. Output of the snippet (`print`) goes to the console as an `output` event, not into the debuggee's stdout. - **Result.** Shown as `variables` shows a value (`showValue`), with `type` and a `variablesReference` where it has children. - **Names.** No new protocol surface: same DAP `evaluate`, same response fields; a compile error of the snippet is the response message (`success: false`) with the checker's text relative to the snippet.
Author
Collaborator

[loop:torbscript] refine: brought to the definition of ready (design decisions in the comment above).

Goal. In the debug console, evaluate runs expressions the restricted evaluator refuses, method and function calls included, by compiling the text like a REPL entry against the stopped frame.

Implements. DEBUGGER.md section 20 ("Evaluate as a snippet") / section 12; reuses REPL.md sections 4, 5, 7. Update sections 12, 15 and 20 of DEBUGGER.md in the same PR (the open item moves to built, the unbuilt rest - deeper frames, statements - stays listed).

Seed. No: no syntax, native, driver flag or string-looked-up std name expected. If the work needs a new kernel operation in runtime/, that is a seed-relevant change and goes in the second commit with the note; the seed refresh stays Torben's.

Acceptance criteria

  • evaluate with context: repl and a text the restricted evaluator refuses (e.g. names.length(), point.distance(origin), double(count)) answers the call's value, type and variablesReference.
  • hover, watch and clipboard contexts, logpoints and breakpoint conditions behave exactly as before (existing tests/debug/*.dap sessions unchanged and green).
  • A snippet that panics, does not compile, or names an unavailable Local answers success: false with a message; the program stays stopped and a following variables request is unchanged.
  • A new session tests/debug/evaluate-snippet.dap + .expected + program dir covering: a method call, a function call, a call on a boxed record, a panic, a compile error, an unavailable name, and a hover that still takes the restricted path.
  • DEBUGGER.md sections 12, 15, 20 updated; torb format --check clean; gates a/b green in forge CI.

Size. One PR, one or two Conventional Commits (feat(debugger): evaluate snippets like a REPL entry plus docs(design): ... if split). Worker: implementer sonnet/high (compiler/src). If the implementer finds the synthesis cannot be shared with compiler/src/repl/synthesis.trb without a larger refactor, a refactor of that size is acceptable (Torben's standing rule), but report it in the PR.

Next: implement. Token usage of this refine worker: about 45k.

[loop:torbscript] refine: brought to the definition of ready (design decisions in the comment above). **Goal.** In the debug console, `evaluate` runs expressions the restricted evaluator refuses, method and function calls included, by compiling the text like a REPL entry against the stopped frame. **Implements.** DEBUGGER.md section 20 ("Evaluate as a snippet") / section 12; reuses REPL.md sections 4, 5, 7. Update sections 12, 15 and 20 of DEBUGGER.md in the same PR (the open item moves to built, the unbuilt rest - deeper frames, statements - stays listed). **Seed.** No: no syntax, native, driver flag or string-looked-up std name expected. If the work needs a new kernel operation in `runtime/`, that is a seed-relevant change and goes in the second commit with the note; the seed refresh stays Torben's. **Acceptance criteria** - [ ] `evaluate` with `context: repl` and a text the restricted evaluator refuses (e.g. `names.length()`, `point.distance(origin)`, `double(count)`) answers the call's value, `type` and `variablesReference`. - [ ] `hover`, `watch` and `clipboard` contexts, logpoints and breakpoint conditions behave exactly as before (existing `tests/debug/*.dap` sessions unchanged and green). - [ ] A snippet that panics, does not compile, or names an unavailable Local answers `success: false` with a message; the program stays stopped and a following `variables` request is unchanged. - [ ] A new session `tests/debug/evaluate-snippet.dap` + `.expected` + program dir covering: a method call, a function call, a call on a boxed record, a panic, a compile error, an unavailable name, and a hover that still takes the restricted path. - [ ] DEBUGGER.md sections 12, 15, 20 updated; `torb format --check` clean; gates a/b green in forge CI. **Size.** One PR, one or two Conventional Commits (`feat(debugger): evaluate snippets like a REPL entry` plus `docs(design): ...` if split). Worker: implementer sonnet/high (`compiler/src`). If the implementer finds the synthesis cannot be shared with `compiler/src/repl/synthesis.trb` without a larger refactor, a refactor of that size is acceptable (Torben's standing rule), but report it in the PR. Next: implement. Token usage of this refine worker: about 45k.
Author
Collaborator

[loop:torbscript] claim implement by session:loop-torbscript-20261023 until 2026-10-23T04:17:00Z

[loop:torbscript] claim implement by session:loop-torbscript-20261023 until 2026-10-23T04:17:00Z
Author
Collaborator

[loop:torbscript] release implement: worker timed out at the lease end (PR #400 is open, not mergeable against current main, CI running); respawning a fresh worker to rebase and finish.

[loop:torbscript] release implement: worker timed out at the lease end (PR #400 is open, not mergeable against current main, CI running); respawning a fresh worker to rebase and finish.
Author
Collaborator

[loop:torbscript] claim implement by session:loop-torbscript-20261023 until 2026-10-23T10:17:00Z

[loop:torbscript] claim implement by session:loop-torbscript-20261023 until 2026-10-23T10:17:00Z
Author
Collaborator

[loop:torbscript] release implement: worker ended at its 30 minute run limit; PR #400 has a review with a real defect (SIGSEGV on the first snippet evaluate, tier A) and red CI. Respawning with a longer run limit.

[loop:torbscript] release implement: worker ended at its 30 minute run limit; PR #400 has a review with a real defect (SIGSEGV on the first snippet evaluate, tier A) and red CI. Respawning with a longer run limit.
Author
Collaborator

[loop:torbscript] claim implement by session:loop-torbscript-20261023 until 2026-10-23T12:06:00Z

[loop:torbscript] claim implement by session:loop-torbscript-20261023 until 2026-10-23T12:06:00Z
argus closed this issue 2026-10-23 08:12:06 +00:00
Author
Collaborator

[loop:torbscript] implement: PR #400 fixed on b78ba7be, CI green (commit messages, gates, bootstrap, tier A, tier B, same-C, playground). next: Argus' re-review of #400.

Review 333, three blockers:

  • Crash (exit 139 on the first names.length() snippet): the snippet's signature wrote the frame's list local as the trait List<String>, a boxed payload with a table, while the frame holds the list's storage words; the nested execute read the words wrong and the debuggee died. writtenTypeNameOf (values.trb) now writes a list, map or set the frame holds as storage as its implementation (ArrayList, TrieMap, TrieSet). The VM-instruction trace commits used to localize it are gone from the history.
  • tests/debug/evaluate-snippet.expected is filled from a complete session (all 24 requests answered) and reviewed: names.length() 3, point.distance(origin) 25, double(count) 42, counter.bump() 1 then 2 and counter.hits 2, the output event snippet says hi before the shout("hi") result, the panic, compile-error and unavailable-name (letters) messages, variables unchanged after the failures except Counter(hits: 2), the program's own line 3 3 21 2 3.
  • format --check is green (the lower.trb line is fixed).

Coverage for the crash class: new session tests/debug/evaluate-snippet-collections (a snippet naming a list, a map and a set local), tier A "tests/debug against .expected" passes with both sessions. History: 6 Conventional Commits on main (301f2149), no merge or revert commits, no kernel operations touched. PR body updated, Closes #39 kept.

[loop:torbscript] implement: PR #400 fixed on b78ba7be, CI green (commit messages, gates, bootstrap, tier A, tier B, same-C, playground). next: Argus' re-review of #400. Review 333, three blockers: - Crash (exit 139 on the first `names.length()` snippet): the snippet's signature wrote the frame's list local as the trait `List<String>`, a boxed payload with a table, while the frame holds the list's storage words; the nested `execute` read the words wrong and the debuggee died. `writtenTypeNameOf` (values.trb) now writes a list, map or set the frame holds as storage as its implementation (`ArrayList`, `TrieMap`, `TrieSet`). The VM-instruction trace commits used to localize it are gone from the history. - `tests/debug/evaluate-snippet.expected` is filled from a complete session (all 24 requests answered) and reviewed: `names.length()` 3, `point.distance(origin)` 25, `double(count)` 42, `counter.bump()` 1 then 2 and `counter.hits` 2, the output event `snippet says hi` before the `shout("hi")` result, the panic, compile-error and unavailable-name (`letters`) messages, `variables` unchanged after the failures except `Counter(hits: 2)`, the program's own line `3 3 21 2 3`. - `format --check` is green (the `lower.trb` line is fixed). Coverage for the crash class: new session `tests/debug/evaluate-snippet-collections` (a snippet naming a list, a map and a set local), tier A "tests/debug against .expected" passes with both sessions. History: 6 Conventional Commits on main (301f2149), no merge or revert commits, no kernel operations touched. PR body updated, `Closes #39` kept.
Sign in to join this conversation.
No labels
loop
ready
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
torbscript/language#39
No description provided.