Skip to content

Fix #3991: COBOL words keep full-width digits and hyphens (- U+FF0D / − U+2212) - #3995

Merged
squid-protocol merged 1 commit into
mainfrom
fix/3991-fullwidth-cobol-words
Sep 29, 2026
Merged

squid-protocol merged 1 commit into
mainfrom
fix/3991-fullwidth-cobol-words

Conversation

@squid-protocol

Copy link
Copy Markdown
Owner

Fixes #3991 (follow-up to #3955 / #3967).

Problem

#3967 made the COBOL name classes accept Unicode letters, but Japanese COBOL words also contain full-width digits 0-9 (U+FF10-FF19) and the full-width hyphen. Shift-JIS byte pair 0x817C decodes to - U+FF0D under cp932 and to − U+2212 under Python's shift_jis. Data names were cut at those characters (項目2 -> 項目, TEST−DATA1 and TEST−RECORD1 both -> TEST). Sections containing them (S−初期化, S-終了, s123) were missing from function_data.

Fix

  • identifiers.py: new WIDE_DIGITS (U+FF10-FF19) and WIDE_HYPHENS (U+FF0D, U+2212). They are built with chr() so the source has no look-alike glyphs (ruff RUF001).
  • languages/cobol.py, core/mainframe_boundary.py (the COBOL readers), core/file_control.py (COBOL FD/SELECT/COPY): every COBOL word class that accepts 0-9 now also accepts WIDE_DIGITS. Every class that accepts the in-word - now also accepts WIDE_HYPHENS, in the same positions. A wide hyphen follows the same placement rules as -, and both (?<![...-]) word-boundary guards now include the wide forms. JCL, IDCAMS, CSD, HLASM and BMS classes are unchanged. No quantifier changed: the classes are wider, but the bounds are the same, so ReDoS safety is unchanged.
  • detector._extract_name: for COBOL, the full-width hyphens are name characters. Before, the regex captured S−初期化 correctly, but name normalisation split it and kept only 初期化.
  • A minus sign between spaced operands (A − B, which COBOL arithmetic requires) is still two words. Literals are untouched.

Tests (tests/core_engine/test_national_names.py)

  • Japanese data names keep full-width digits and hyphens, and TEST−DATA1 / TEST−RECORD1 no longer collide (REDEFINES target included).
  • All five sections from the issue's reproduction are found whole.
  • PERFORM and GO TO callees are read whole.
  • Both decodings of SJIS 0x817C: a cp932-encoded file is read through read_source as shift_jis (U+2212) and as cp932 (U+FF0D), and both give whole names.
  • A spaced full-width minus is not part of a name.
  • Structural guard: every word class in cobol.py that takes 0-9 / - also takes WIDE_DIGITS / WIDE_HYPHENS. The existing NATIONAL-splice test now allows the wide constants after NATIONAL.

Verification

  • The issue's reproduction now gives 項目2, O−文字列, WK-03, 横浜-1 and 主処理, ASCII-SEC, S−初期化, s123, S-終了.
  • crucible_check.py: full_precision PASS, zero_dependency PASS. The golden masters do not change, so there is no re-bless. The crucible has no full-width COBOL words.
  • ground_truth_ledger.py check: ledger clean (0 open defects).
  • Targeted tests (COBOL/JCL/HLASM/BMS extraction + strict/ReDoS, every test that imports mainframe_boundary/file_control, source_text, EBCDIC DBCS, unicode gauntlet): 1337 passed, 2 skipped, 2 xfailed, 3 xpassed.
  • ruff_audit / mypy_audit / dead_key_audit --ci: no new findings. The changed files pass ruff format.

opensourcecobol4j fresh estate (#3806), independent Unicode-aware reading vs the master DB

suite Japanese data names truncated (main -> this PR) Japanese sections/paragraphs missing (main -> this PR)
i18n_sjis 14 -> 0 9 -> 1
cobol_utf8 7 -> 0 12 -> 1
i18n_utf8 4 -> 0 7 -> 1

The one remaining "missing" entry per suite comes from the analyzer, not the engine. In 003-long-field-name/prog.cob, the data name 項目ABCDEFGH01234… sits alone on its own line as the operand of MOVE "Unicode" TO (and of DISPLAY). The analyzer's paragraph regex reads it as a paragraph header. It is a data item, and it is now recorded whole in record_data. The refract → cobol-to-java → mvn compile results are unchanged from the baseline scan: cobol_utf8, i18n_sjis and i18n_utf8 compile. jp-compat still fails with the same 20 pre-existing cannot find symbol: class TestFileDTO errors as the baseline scan. Those errors are unrelated to this change.

🤖 Generated with Claude Code

https://claude.ai/code/session_01EC8FWfn3aPmsUPnRgKNVup

… − U+2212)

#3967 let a COBOL word hold any Unicode letter, but a Japanese word also holds
full-width digits (0-9) and the full-width hyphen, which SJIS 0x817C decodes to
U+FF0D under cp932 and U+2212 under Python's shift_jis. Data names were cut at
those characters (項目2 -> 項目, TEST−DATA1 -> TEST) and sections holding
them (S−初期化, s123) were missing from function_data.

- identifiers.py: WIDE_DIGITS / WIDE_HYPHENS.
- cobol.py, mainframe_boundary.py (COBOL readers), file_control.py (COBOL FD /
  SELECT / COPY): every COBOL word class that takes 0-9 takes WIDE_DIGITS, and
  every one that takes the in-word '-' takes WIDE_HYPHENS, at the same positions.
  JCL / IDCAMS / CSD / HLASM / BMS classes are unchanged. Quantifiers unchanged.
- detector._extract_name: for COBOL the full-width hyphens are name characters,
  so `S−初期化` is not reduced to its tail `初期化`.
- tests: data names, sections, callees, both decodings of 0x817C from a
  cp932-encoded file, spaced minus stays arithmetic, and a structural check that
  every cobol.py word class carries the wide characters.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EC8FWfn3aPmsUPnRgKNVup
@github-actions

Copy link
Copy Markdown
Contributor

🐦‍⬛ Muninn Security Scan

✅ No security issues found.

🐦‍⬛ Powered by Muninn · Skald Lab

@squid-protocol
squid-protocol merged commit 71d33c5 into main Sep 29, 2026
37 checks passed
@squid-protocol
squid-protocol deleted the fix/3991-fullwidth-cobol-words branch September 29, 2026 13:09
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.

COBOL: full-width hyphens (- U+FF0D / − U+2212) and full-width digits still truncate Japanese data names and drop sections (#3955 follow-up)

1 participant