Skip to content

Commit eb18de1

Browse files
dspl1236claude
andcommitted
dcl: read a (abs) and A (average); correct the previous commit's count
The count in 1ed003b was wrong. It came from calling evaluable() on the tuples expressions() yields, (offset, path, string), rather than on the strings. Iterating a tuple finds no recognised tokens, so evaluable() returned True vacuously and every expression looked evaluable. The real figure at that commit was 23 of 25 distinct expression strings, not 52 of 52. a and A are named by the interpreter's own trace strings: 'absolut %d; ' at 0952ACB4 and 'Average; ' at 0952ACC4. a is absolute value, unary in place -- 08906A26 loads @(4,r9) as the argument and 08906A28 stores the result back to it, leaving the depth alone. Implemented, so xa' now evaluates. A is read but deliberately left unimplemented. It averages only when it is the expression's last character, or second-to-last followed by ' (08906A6A / 08906A72 / 08906A7C) -- exactly the shape xA'. It then drains the stack into a sum at r14+124 with a count in r13 and divides via __sdivsi3_i4. But the depth-- at 08906A82 precedes the loop guard, so a one-element stack never enters the loop and the count stays zero: a bare xA' divides by zero. A only yields a value once the dataflow node array at ctx+84 contributes, which is the same reason the nine-placeholder expressions cannot be evaluated standalone. Refusing is the correct result, not a gap in the reading. Also corrects an earlier claim: the scan that reported no cmp/eq #39 anywhere in the interpreter used the linear disassembly, which halts at the first literal pool and so never reached the handler. Per-word disassembly finds one at 08906A7C, inside A. The conclusion that ' is a no-op still holds -- it rests on the shared loop tail at 08906C1A, not on that scan. 24 of 25 distinct expressions now evaluate. Coverage stays 100.00% over 10846 records. Still refused: ! i o r m, whose handlers have not been read. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent 1ed003b commit eb18de1

1 file changed

Lines changed: 35 additions & 4 deletions

File tree

pcmexplorer/dcl.py

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -566,7 +566,7 @@ def _sx(v):
566566

567567
#: Tokens whose behaviour has been read out of the handler machine code. Every
568568
#: other recognised character is deliberately unimplemented -- see `evaluate`.
569-
RPN_VERIFIED = frozenset("+-*/%^~&|=<>?xDdM")
569+
RPN_VERIFIED = frozenset("+-*/%^~&|=<>?axDdM")
570570

571571
#: Two-character tokens whose handlers have been read.
572572
RPN_VERIFIED_DIGRAPHS = frozenset(("<<", ">>", "<=", ">=",
@@ -673,9 +673,31 @@ def evaluate(expr, ops, memory=0, strict=True):
673673
The sole exception is an empty stack, where the arm is entered with T set
674674
from a ``tst r12,r12`` delay slot and exits through the error report.
675675
676-
The rounding, averaging and inversion tokens (``!``, ``i``, ``o``, ``r``,
677-
``a``, ``A``, ``m``) have handlers whose stack effects were not read, and
678-
still raise. A previous version guessed at all of these and claimed
676+
``a`` and ``A`` are named by the interpreter's own trace strings --
677+
``'absolut %d; '`` at ``0952ACB4`` and ``'Average; '`` at ``0952ACC4``.
678+
679+
``a`` is absolute value, unary in place, and is implemented.
680+
681+
``A`` is **read but deliberately not implemented.** It averages only when it
682+
is the last character of the expression, or the second-to-last followed by
683+
``'`` (``08906A6A`` / ``08906A72`` / ``08906A7C``) -- exactly the shape
684+
``xA'``. It then drains the stack into a sum at ``r14+124`` with a count in
685+
r13 and divides through ``__sdivsi3_i4``. But the ``depth--`` at ``08906A82``
686+
happens *before* the loop guard, so on a one-element stack the loop never
687+
runs and the count stays zero: a bare ``xA'`` would divide by zero. The
688+
operator only yields a value once the dataflow node array at ``ctx+84``
689+
contributes, which is the same reason the nine-placeholder expressions
690+
cannot be evaluated standalone. Refusing is the correct outcome here, not a
691+
gap in the reading.
692+
693+
Both also consume more than one character: ``a`` advances the index by two
694+
at ``08906A2E`` on top of the tail's one. In the shipped data its only
695+
occurrence is ``xa'``, where what it swallows is the no-op ``'`` and the end
696+
of the string, so the value is unaffected either way.
697+
698+
The rounding and inversion tokens (``!``, ``i``, ``o``, ``r``, ``m``) have
699+
handlers whose stack effects were not read, and still raise.
700+
A previous version guessed at all of these and claimed
679701
confirmation against "node 187", which turned out to be an artifact of a
680702
keying bug in :func:`operands` -- the expression at that ident is not what
681703
was assumed. Refusing is better than returning a number nobody can trust.
@@ -785,6 +807,15 @@ def evaluate(expr, ops, memory=0, strict=True):
785807
raise ValueError("stack underflow at '~' in %r" % expr)
786808
st.append(_sx(~st.pop()))
787809
continue
810+
if tok == "a":
811+
if not st:
812+
raise ValueError("stack underflow at 'a' in %r" % expr)
813+
# Absolute value, unary in place: 08906A26 loads @(4,r9) as the
814+
# argument and 08906A28 stores the result straight back to it, so
815+
# the depth is untouched. Named by its own trace string,
816+
# 'absolut %d; ' at 0952ACB4.
817+
st.append(_sx(abs(st.pop())))
818+
continue
788819
if tok in ("&&", "||"):
789820
if len(st) < 2:
790821
raise ValueError("stack underflow at %r in %r" % (tok, expr))

0 commit comments

Comments
 (0)