fix: 高密度テスト52/52通過 + SPACES figurative constant FP fix

COBOL技術者による高密度テスト(52 tests)実装:

発見・修正されたバグ:
1. WS-KEY = SPACES の figurative constant 比較が FP 原因
   - _matches_key_comparison に figurative constant除外を追加
   - 構造検知の信号4でも SPACES/ZERO 等を除外
   - structural_matching で単一ファイルプログラムを除外

2. simple_vs_two_stage が常に単純マッチングを返していた
   - 実証拠なしでも0.5で返す → 他の分類を汚染
   - 修正: file_count>=2 + IF + 比較証拠がない場合は unknown

3. simple_vs_two_stageテストを現実に合わせて更新

回帰: 767 passed(0 new failures)
高密度テスト: 52/52 PASS
This commit is contained in:
NB-076
2026-06-21 17:04:48 +08:00
parent ecf3c1cd61
commit a784c6974a
4 changed files with 49 additions and 12 deletions
+24 -3
View File
@@ -75,10 +75,15 @@ def _matches_key_comparison(source_upper: str) -> bool:
"""
# 模式 1: KEY 变量出现在比较上下文中(= < > 后跟变量)
# 注意: 不能用 \s 代替 [=<>],否则「WS-KEY PIC」中的空格也会误匹配
if re.search(r'(?:WS-[\w-]*KEY[A-Z0-9-]*|WS[A-Z0-9]*KEY[A-Z0-9]*)\s*[=<>]', source_upper):
# 排除: 右边的 Figurative Constant (SPACES, ZERO, HIGH-VALUE 等)
_figurative = r'(?:SPAC?E?S?|ZERO[S]?E?S?|HIGH[-\s]VALUE[S]?|LOW[-\s]VALUE[S]?|'
_figurative += r'NULL[S]?|QUOTE[S]?|ALL\s+\'[^\']*\')'
if re.search(r'(?:WS-[\w-]*KEY[A-Z0-9-]*|WS[A-Z0-9]*KEY[A-Z0-9]*)\s*[=<>]'
r'(?!\s*' + _figurative + r')', source_upper):
return True
# 模式 2: 非 WS- 前缀的 KEY 变量(旧式命名 K01-KEY 等)
if re.search(r'\b[A-Z]\d{0,2}-[\w-]*KEY\s*[=<>]', source_upper):
if re.search(r'\b[A-Z]\d{0,2}-[\w-]*KEY\s*[=<>]'
r'(?!\s*' + _figurative + r')', source_upper):
return True
# 模式 3: 源码中含有 READ INTO + KEY 变量
if re.search(r'READ\s+\w+\s+INTO\s+\w+.*KEY', source_upper, re.DOTALL):
@@ -139,7 +144,10 @@ def _detect_matching_structure(source_upper: str) -> float:
# 信号 4: IF 比较两个不同变量(跨文件字段比较,任何命名风格)
# K1 = K2 (简单名), CUST-CODE = ORDR-CODE (连字号), WS-KEY1 = WS-KEY2
if re.search(r'IF\s+\w[\w-]*\s*[=<>]\s*\w[\w-]*', source_upper):
# 排除右侧为 figurative constant (SPACES, ZERO, HIGH-VALUE 等)
_fig = r'(?:SPACES?|ZERO[S]?E?S?|HIGH[-\s]VALUE[S]?|LOW[-\s]VALUE[S]?|NULL[S]?|QUOTE[S]?)'
if re.search(r'IF\s+\w[\w-]*\s*[=<>]\s+\w[\w-]*', source_upper) and \
not re.search(r'IF\s+\w[\w-]*\s*[=<>]\s+' + _fig, source_upper):
signals += 1
# 信号 5: 2+ 文件 OPEN INPUT
@@ -148,6 +156,19 @@ def _detect_matching_structure(source_upper: str) -> float:
signals += 1
# 确信度: 6 中 5+ = 0.55, 4 = 0.50, 3 = 0.40
# 单文件程序(无多文件特征)降级确信度
has_multi_file = bool(re.search(r'OPEN\s+INPUT\s+\w+\s+\w+', source_upper)) or \
len(re.findall(r'\bFD\s+\w+', source_upper)) >= 2 or \
len(re.findall(r'SELECT\s+\w+', source_upper)) >= 2
if not has_multi_file:
# 单文件: 仅当有明显键比较(非 figurative constant)时才保留低确信度
_fig = r'(?:SPACES?|ZERO[S]?E?S?|HIGH[-\s]VALUE[S]?|LOW[-\s]VALUE[S]?)'
has_real_key_cmp = bool(re.search(r'IF\s+\w[\w-]*\s*[=<>]\s+\w[\w-]*', source_upper)) and \
not bool(re.search(r'IF\s+\w[\w-]*\s*[=<>]\s+' + _fig, source_upper))
if has_real_key_cmp and re.search(r'READ\s+\w+', source_upper):
pass # 有键比较+文件读取 → 可能是极简匹配,保留
else:
signals -= 2 # 无多文件特征 → 大幅降级
if signals >= 5:
return 0.55
elif signals >= 4: