feat: SQL between/hostvar-key alignment, class-condition parsing, gcov merge across scenario runs
This commit is contained in:
+67
-6
@@ -207,13 +207,13 @@ _RE_SQL_INC = re.compile(
|
||||
_BUILTIN_SQLCA = """\
|
||||
01 SQLCA.
|
||||
05 SQLCAID PIC X(8).
|
||||
05 SQLCABC PIC S9(9) COMP.
|
||||
05 SQLCODE PIC S9(9) COMP.
|
||||
05 SQLCABC PIC S9(9) COMP-5.
|
||||
05 SQLCODE PIC S9(9) COMP-5.
|
||||
05 SQLERRM.
|
||||
10 SQLERRML PIC S9(4) COMP.
|
||||
10 SQLERRML PIC S9(4) COMP-5.
|
||||
10 SQLERRMC PIC X(70).
|
||||
05 SQLERRP PIC X(8).
|
||||
05 SQLERRD OCCURS 6 TIMES PIC S9(9) COMP.
|
||||
05 SQLERRD OCCURS 6 TIMES PIC S9(9) COMP-5.
|
||||
05 SQLWARN.
|
||||
10 SQLWARN0 PIC X.
|
||||
10 SQLWARN1 PIC X.
|
||||
@@ -523,6 +523,11 @@ def parse_pic(pic_str: str) -> PicInfo:
|
||||
elif expanded[0] == 'A':
|
||||
info.type = 'alphabetic'
|
||||
info.length = len(expanded)
|
||||
elif expanded[0] == 'N':
|
||||
# PIC N(n) = national/DBCS: 每个字符占 2 字节(GnuCOBOL 实际存储)。
|
||||
# 按 n 字节计算会导致 FD 记录布局/偏移错位(如 N(040)=80B 却按 40B 写)。
|
||||
info.type = 'national'
|
||||
info.length = len(expanded) * 2
|
||||
elif expanded[0] in ('Z', '*', '$', '+', '-'):
|
||||
info.type = 'numeric-edited'
|
||||
info.digits = expanded.count('9')
|
||||
@@ -597,15 +602,17 @@ def parse_file_control(source: str) -> dict:
|
||||
result = {}
|
||||
for sel_m in re.finditer(
|
||||
r'SELECT\s+(\w[\w-]*)\s+[^.]*?\bASSIGN\s+TO\s+'
|
||||
r'(?:(["\'])(.*?)\2|(\w[\w-]*))'
|
||||
r'(?:(["\'])(.*?)\2|EXTERNAL\s+(\w[\w-]*)|(\w[\w-]*))'
|
||||
r'[^.]*\.',
|
||||
fc, re.IGNORECASE
|
||||
):
|
||||
name = sel_m.group(1).upper()
|
||||
if sel_m.group(2):
|
||||
assign_to = sel_m.group(3).upper()
|
||||
else:
|
||||
elif sel_m.group(4):
|
||||
assign_to = sel_m.group(4).upper()
|
||||
else:
|
||||
assign_to = sel_m.group(5).upper()
|
||||
clause = sel_m.group(0)
|
||||
org_m = re.search(r'ORGANIZATION\s+(LINE\s+)?SEQUENTIAL', clause, re.IGNORECASE)
|
||||
if org_m and org_m.group(1):
|
||||
@@ -673,3 +680,57 @@ def scan_open_statements(source: str) -> dict:
|
||||
for fname in re.findall(r'\w[\w-]*', seg_m.group(2)):
|
||||
dirs[fname.upper()] = direction
|
||||
return dirs
|
||||
|
||||
|
||||
def scan_sort_merge_directions(source: str) -> dict:
|
||||
"""Parse MERGE / SORT statements, returns {file_name: 'INPUT'|'OUTPUT'}.
|
||||
|
||||
MERGE/SORT 的 USING 文件不会被 OPEN(由语句隐式打开),因此
|
||||
scan_open_statements 无法识别。此函数扫描 PROCEDURE DIVISION 中
|
||||
MERGE ... USING <f1> <f2> 与 SORT ... USING <f> GIVING <g> 子句,
|
||||
将 USING 文件识别为 INPUT、GIVING 文件识别为 OUTPUT。
|
||||
|
||||
兼容性:对任何含 MERGE/SORT 的程序通用,非 MERGE/SORT 程序返回空 dict。
|
||||
"""
|
||||
dirs = {}
|
||||
_KEYWORDS = {'USING', 'GIVING', 'PROCEDURE', 'KEY', 'ASCENDING', 'DESCENDING', 'ON', 'OUTPUT', 'INPUT'}
|
||||
for m in re.finditer(
|
||||
r'\b(MERGE|SORT)\s+\w[\w-]*\s+.*?\b(?:USING|GIVING)\s+(.+?)\s*(?:OUTPUT|INPUT)?\s*PROCEDURE\s+\w[\w-]*\s*\.',
|
||||
source, re.IGNORECASE | re.DOTALL
|
||||
):
|
||||
stmt = m.group(0)
|
||||
kind = m.group(1).upper()
|
||||
files_raw = m.group(2)
|
||||
files = re.findall(r'\b(\w[\w-]*)\b', files_raw)
|
||||
for fn in files:
|
||||
fn = fn.upper()
|
||||
if fn not in _KEYWORDS:
|
||||
dirs[fn] = 'INPUT'
|
||||
if kind == 'SORT':
|
||||
g = re.search(r'\bGIVING\s+(\w[\w-]*)', stmt, re.IGNORECASE)
|
||||
if g:
|
||||
dirs[g.group(1).upper()] = 'OUTPUT'
|
||||
# 单行回退:无 PROCEDURE 的 MERGE/SORT(如 MERGE F ON KEY K USING A B.)
|
||||
for m in re.finditer(
|
||||
r'\b(MERGE|SORT)\s+\w[\w-]*\s+.*?\b(?:USING|GIVING)\s+(.+?)\.',
|
||||
source, re.IGNORECASE
|
||||
):
|
||||
kind = m.group(1).upper()
|
||||
files_raw = m.group(2)
|
||||
files = re.findall(r'\b(\w[\w-]*)\b', files_raw)
|
||||
for fn in files:
|
||||
fn = fn.upper()
|
||||
if fn not in _KEYWORDS:
|
||||
dirs[fn] = 'INPUT'
|
||||
if kind == 'SORT':
|
||||
g = re.search(r'\bGIVING\s+(\w[\w-]*)', m.group(0), re.IGNORECASE)
|
||||
if g:
|
||||
dirs[g.group(1).upper()] = 'OUTPUT'
|
||||
return dirs
|
||||
|
||||
|
||||
def scan_all_file_directions(source: str) -> dict:
|
||||
"""OPEN 方向 + MERGE/SORT USING/GIVING 方向的合并(输入方向全集)。"""
|
||||
dirs = scan_open_statements(source)
|
||||
dirs.update(scan_sort_merge_directions(source))
|
||||
return dirs
|
||||
|
||||
Reference in New Issue
Block a user