Skip to content

Commit a8cabe2

Browse files
Merge pull request #50 from glpython-edu/feature/unary-minus-name
Visualizer: no step for -x where it reads as its value; console check without buttons
2 parents d78944a + 848daa7 commit a8cabe2

5 files changed

Lines changed: 34 additions & 5 deletions

File tree

‎CLAUDE.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ There is no unit test framework. The automated checks are `test:protocol` (plain
5555
- Values are shown through `_glpython_safe_repr`, which writes types as learners do (`int`, not `<class 'int'>`); the program's own output keeps Python's repr.
5656
- It abbreviates long values (`[1, 2, 3, 4, 5, …, 100]`; levels in `_GLPYTHON_ABBREVIATION_LEVELS`) and notes each full text. The next recorded step gets `fullValues` (`{shown text: index}`), the trace result `values` (`{text, cut, length, kind}`). The UI (`fullValuesUpTo`, `ValueText`, `LocalsValue`, `FullValueDialog`) opens one on click.
5757
- A name indexed in a subscript (`dauer` in `dauer[1]`, `_glpython_indexed`) gets no step of its own, so `dauer[1]` is one step to the element.
58-
- A display (`[…]`, `(…)`, `{…}`) gets no step of its own (`_glpython_record_display`), only where its value reads differently (unpacking, duplicates); nor does a negative number literal (`_glpython_is_negative_number`). Every step must change something.
58+
- A display (`[…]`, `(…)`, `{…}`) gets no step of its own (`_glpython_record_display`), only where its value reads differently (unpacking, duplicates); nor does a negative number literal (`_glpython_is_negative_number`), or `-name` where it already reads as its value. Every step must change something.
5959

6060
Worker messages:
6161
- `run` → `result`/`error`; while it runs, `output` messages (`{id, text}`, `id` undefined for a program, the input's id for a REPL input) stream what was printed, at most 50 per 50 ms. The final message brings the whole output again.

‎README.md‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,9 @@ no steps at all, nested at any depth and with negative numbers (`-3`, `-2.5`):
142142
only the parts that change have steps: `[1 + 2, 3, 4] → [3, 3, 4]`,
143143
`["+", 1, ["*", n, 3]] → ["+", 1, ["*", 2, 3]]`. A display keeps its step only
144144
where its value reads differently: unpacking (`[*a, 2] → [1, 2]`) and a set or
145-
dict whose duplicates go (`{1, 1} → {1}`).
145+
dict whose duplicates go (`{1, 1} → {1}`). Likewise `-x` with `x = 7` is one
146+
step, to `-7`; the minus has a step of its own only where it changes what is
147+
shown (`-x` with `x = -7` reads `--7`, then `7`).
146148

147149
A long value is abbreviated, in the step and in the variables panel alike,
148150
keeping its first elements and its last one: `[1, 2, 3, 4, 5, …, 100]`.

‎scripts/check-console-browser.mjs‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,15 @@ try {
167167
await page.waitForFunction(() => window.ready, null, { timeout: 120000 })
168168
return page
169169
}
170-
const consoleText = (frame) => frame.locator('.console').innerText()
170+
// The transcript as it reads, without the entries' Visualize buttons: next
171+
// to a ">>>" line or on a line of their own, depending on the width.
172+
const consoleText = (frame) => frame.locator('.console').evaluate((console) => {
173+
const buttons = [...console.querySelectorAll('.repl-entry-visualize')]
174+
for (const button of buttons) button.style.display = 'none'
175+
const text = console.innerText
176+
for (const button of buttons) button.style.display = ''
177+
return text
178+
})
171179
const programLine = (frame) => frame.locator('.repl-line--program .repl-input')
172180
// A REPL input, once the REPL is free again (until then it keeps the text).
173181
const enterRepl = async (frame, text) => {

‎scripts/check-runtime-lists.py‎

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,13 +121,26 @@ def steps_on_line(result, line):
121121
("a = 5", "w = [a, 2]", [("a", "5")], "[5, 2]"),
122122
("x = 7", "w = [[1, 2], x]", [("x", "7")], "[[1, 2], 7]"),
123123
("n = 2", 'w = ["+", 1, ["*", n, 3]]', [("n", "2")], "['+', 1, ['*', 2, 3]]"),
124-
("n = 2", "w = (n, -n, {n: [n]})", [("n", "2"), ("n", "2"), ("-2", "-2"), ("n", "2"), ("n", "2")], "(2, -2, {2: [2]})"),
124+
("n = 2", "w = (n, -n, {n: [n]})", [("n", "2"), ("n", "2"), ("n", "2"), ("n", "2")], "(2, -2, {2: [2]})"),
125125
("", "w = [len([1, 2]), 3]", [("len(...)", "2")], "[2, 3]"),
126126
]:
127127
result = trace(f"{setup}\n{line}\n")
128128
expect(f"one step each: {line}", evals_on_line(result, 2), expected)
129129
expect(f"the assignment: {line}", [label for kind, label, _ in steps_on_line(result, 2) if kind == "exec"], [f"w = {value}"])
130130

131+
# "-x" is one step, to x's value, where that already reads as the result;
132+
# where it doesn't ("--7", "-True", "-(a + b)"), the minus has its step.
133+
for setup, line, expected in [
134+
("x = 7", "w = -x", [("x", "7")]),
135+
("x = 2.5", "w = -x", [("x", "2.5")]),
136+
("x = -7", "w = -x", [("x", "-7"), ("--7", "7")]),
137+
("x = True", "w = -x", [("x", "True"), ("-True", "-1")]),
138+
("a = 1\nb = 2", "w = -(a + b)", [("a", "1"), ("b", "2"), ("1 + 2", "3"), ("-3", "-3")]),
139+
]:
140+
result = trace(f"{setup}\n{line}\n")
141+
row = setup.count("\n") + 2
142+
expect(f"unary: {setup}; {line}", evals_on_line(result, row), expected)
143+
131144
# Where the value reads differently from the display, the display has its
132145
# step: unpacking, and duplicates in a set or dict. Unpacking as in Python.
133146
for setup, line, expected in [

‎src/runtime/visualizer.py‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -611,7 +611,13 @@ def _eval_unaryop(node, scope):
611611
if _glpython_is_negative_number(node):
612612
return value
613613
symbol = _GLPYTHON_OP_SYMBOLS.get(type(node.op), "?")
614-
_glpython_record(node, "eval", f"{symbol}{_glpython_safe_repr(operand, 40)}", _glpython_safe_repr(value), _glpython_current_func())
614+
text = f"{symbol}{_glpython_safe_repr(operand, 40)}"
615+
value_text = _glpython_safe_repr(value)
616+
# "-x" with x = 7 already reads "-7" once x is replaced: no step that
617+
# changes nothing (but "-x" with x = -7 reads "--7", which becomes 7).
618+
if isinstance(node.op, ast.USub) and isinstance(node.operand, ast.Name) and text == value_text:
619+
return value
620+
_glpython_record(node, "eval", text, value_text, _glpython_current_func())
615621
return value
616622

617623
def _eval_boolop(node, scope):

0 commit comments

Comments
 (0)