Skip to content

fix(as370): &SYSLIST(n,m) reaches inside a sublist operand (#94) - #96

Merged
mgrossmann merged 1 commit into
mainfrom
fix/syslist-second-subscript
Aug 29, 2026
Merged

fix(as370): &SYSLIST(n,m) reaches inside a sublist operand (#94)#96
mgrossmann merged 1 commit into
mainfrom
fix/syslist-second-subscript

Conversation

@mgrossmann

Copy link
Copy Markdown
Contributor

Closes #94.

&SYSLIST(n) is the n'th positional operand; &SYSLIST(n,m) is the m'th element of that operand's sublist. The resolver evaluated the whole subscript text as a single expression — eval_seta("1,1") stops at the comma and yields 1 — so the second subscript was dropped and the reference returned the operand entire, parentheses included.

Used as a name, the generated statement carried (ALPHA,8,GAMMA) and the module died of IFO016, an over-long name field. That diagnostic was correct about what it saw and pointed nowhere near the cause: nothing in the source has an over-long name.

Measured, not assumed

capture.py on mvsdev, JOB02901, RC 0 — deck and listing both committed (tests/ref/syslist.obj, tests/listref/ifox-listing-syslist.txt). The fixture probes a three-element sublist and a plain operand on purpose, because the plain one decides whether per-level substitution needs a special case:

(n,1) (n,2) (n,3)
operand 1 = (ALPHA,8,GAMMA) ALPHA 8 GAMMA
operand 2 = BETA BETA null

It does not: sub_elem already returns a value with no leading ( for index 1 and nothing beyond, which is exactly what the guest does. The deck is byte-identical through ESD and TXT, the END card differing only in the translator IDR as everywhere else.

The first capture attempt was rejected — 22 statements flagged, IFO006 UNDEFINED VARIABLE SYMBOL. Assembler XF requires LCLA/LCLC for a macro's local SET symbols and as370 accepts them undeclared. A separate leniency, noted in the commit and the listref README, not fixed here.

The change

One subscript per level, applying sub_elem once each. The scan is also balanced rather than strchr(')'): a subscript may carry its own parentheses — &SYSLIST((&I+1),1) — and the first ) is then in the wrong place. syslist_nested covers that on its own.

The named-parameter form &P(m) was already right and stays in the fixture as a control; it shares sub_elem and only ever needs one level.

sub_elem's tail copy moves from strncpy to the file's own scopy. gcc 14.2 reads the pair as a truncating copy once the new caller makes the sizes visible at the inline site — out holds 96 bytes and v can be the whole &SYSLIST buffer. The truncation is intended and bounded as it always was; scopy says so and terminates.

Verification

deck vs. IFOX00 byte-identical through ESD + TXT
tests/corpus/check.sh no module moved
826 ecosystem modules unchanged
tests/run.sh 49 OK, 0 failures, ALL SAMPLES BYTE-IDENTICAL TO IFOX00
tests/listref/check.sh all references still column-exact

Both new cases fail against main (expected RC 0, got 8 / a parenthesised subscript truncated the expression).

On a local tree of real IBM MVS assembler — not committed here — 6 more of a 277-module spread sample assemble clean, none lost. Measured against main including #93, so this is #94's own contribution.

Built warning-clean with real gcc 14.2 -Werror on mvsdev as well as clang; it caught the strncpy truncation above, which clang accepts.

&SYSLIST(n) is the n'th positional operand; &SYSLIST(n,m) is the m'th element of
that operand's sublist. The resolver evaluated the whole subscript text as a
single expression -- eval_seta("1,1") stops at the comma and yields 1 -- so the
second subscript was dropped and the reference returned the operand entire,
parentheses included.

Where the result was used as a name, the generated statement carried
`(ALPHA,8,GAMMA)` and the module died of IFO016, an over-long name field. That
diagnostic was correct about what it saw and pointed nowhere near the cause:
nothing in the source has an over-long name.

One subscript per level now, applying sub_elem once each. The scan is also
balanced rather than strchr(')'), since a subscript may carry its own
parentheses -- &SYSLIST((&I+1),1) -- and the first ')' is then in the wrong
place; its own test case covers that.

Measured on the guest rather than assumed (JOB02901, deck and listing committed
as tests/ref/syslist.obj and tests/listref/ifox-listing-syslist.txt). The fixture
probes a three-element sublist AND a plain operand, because the plain one decides
whether per-level substitution needs a special case:

  operand 1 = (ALPHA,8,GAMMA)   (1,1)=ALPHA  (1,2)=8  (1,3)=GAMMA
  operand 2 = BETA              (2,1)=BETA   (2,2)=null

It does not: sub_elem already returns a value with no leading '(' for index 1 and
nothing beyond, which is exactly what the guest does. The deck now matches IFOX00
byte for byte through the ESD and TXT cards, the END card differing only in the
translator IDR as everywhere else.

The named-parameter form &P(m) was already right and stays a control in the
fixture; it shares sub_elem and only ever needs one level.

sub_elem's tail copy moves from strncpy to the file's own scopy. gcc 14.2 reads
the pair as a truncating copy once the new caller makes the sizes visible at the
inline site -- OUT holds 96 bytes and V can be the whole &SYSLIST buffer. The
truncation is intended and bounded as it always was; scopy says so and
terminates.

The ecosystem is untouched -- 826 modules unchanged, no corpus module moved. On a
local tree of real IBM MVS assembler, 6 more of a 277-module sample assemble
clean, none lost.

Noted while capturing, and not fixed here: IFOX rejected the first version of the
fixture with 22 IFO006 UNDEFINED VARIABLE SYMBOL, because Assembler XF requires
LCLA/LCLC for a macro's local SET symbols and as370 accepts them undeclared.
@mgrossmann
mgrossmann merged commit ba73d4d into main Aug 29, 2026
2 checks passed
@mgrossmann
mgrossmann deleted the fix/syslist-second-subscript branch August 29, 2026 20:31
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.

as370: &SYSLIST(n,m) drops the second subscript — a sublist operand is substituted whole

1 participant