merge local cobol_testgen improvements into v3 shared modules
- cond.py: SQLCODE/SQLSTATE handling, alphanumeric >/< boundary fix - output.py: termination tracking, db_input support, _is_field_assigned filter - coverage.py: mark_from_gcov, THRU support, KeyError protection - gcov.py: new file (dependency for coverage.py) - grammar.lark: multi-segment PIC support - read.py: SQL INCLUDE resolution, DECLARE TABLE parsing, * comment fix - core.py: SQL parsing, blocked_names, keyword list - design.py: multi-sentinel, THRU ranges, PERFORM VARYING last iteration - __init__.py: local main() + v3 API functions, guarded imports All 6 ZAN programs verified passing through v3 pipeline
This commit is contained in:
+45
-3
@@ -44,12 +44,34 @@ def parse_single_condition(text, fields=None):
|
||||
- Bare: WS-EOF → (WS-EOF, '=', 'Y')
|
||||
- NOT bare: NOT WS-EOF → (WS-EOF, '<>', 'Y')
|
||||
- NOT arith: A+B NOT = C → ('A+B', '<>', 'C')
|
||||
- SQLCODE: SQLCODE = 100 → ('SQLCODE', '=', '100')
|
||||
- SQLSTATE: SQLSTATE <> '02000' → ('SQLSTATE', '<>', '02000')
|
||||
|
||||
Returns None for compound (AND/OR) conditions.
|
||||
"""
|
||||
if ' AND ' in text or ' OR ' in text:
|
||||
return None
|
||||
text = text.strip()
|
||||
field_name = text.split()[0] if text else ''
|
||||
|
||||
# SQLCODE special handling
|
||||
if field_name.upper() == 'SQLCODE':
|
||||
text_upper = text.upper()
|
||||
if 'GREATER THAN 0' in text_upper or 'GREATER THAN ZERO' in text_upper:
|
||||
return ('SQLCODE', '>', '0')
|
||||
if 'LESS THAN 0' in text_upper:
|
||||
return ('SQLCODE', '<', '0')
|
||||
if '= 100' in text_upper:
|
||||
return ('SQLCODE', '=', '100')
|
||||
if 'NOT = 100' in text_upper:
|
||||
return ('SQLCODE', '<>', '100')
|
||||
|
||||
# SQLSTATE special handling
|
||||
if field_name.upper() == 'SQLSTATE':
|
||||
normalized_sql = re.sub(r'\bNOT\s*=', '<>', text, flags=re.IGNORECASE)
|
||||
m = re.match(r"SQLSTATE\s*(>=|<=|<>|>|<|=)\s*['\"]?(.+?)['\"]?\s*$", normalized_sql, re.IGNORECASE)
|
||||
if m:
|
||||
return ('SQLSTATE', m.group(1), m.group(2).strip().strip("'\""))
|
||||
|
||||
# Resolve 88-level condition names
|
||||
if fields:
|
||||
@@ -62,9 +84,9 @@ def parse_single_condition(text, fields=None):
|
||||
|
||||
# Bare NOT field reference (no operator): NOT WS-EOF → WS-EOF <> 'Y'
|
||||
if text.upper().startswith('NOT ') and not re.search(r'(>=|<=|<>|>|<|=)', text):
|
||||
field_name = text[4:].strip()
|
||||
if re.match(r'^[A-Z][A-Z0-9-]*(?:\([^)]*\))?$', field_name, re.IGNORECASE):
|
||||
return (field_name, '<>', 'Y')
|
||||
fn = text[4:].strip()
|
||||
if re.match(r'^[A-Z][A-Z0-9-]*(?:\([^)]*\))?$', fn, re.IGNORECASE):
|
||||
return (fn, '<>', 'Y')
|
||||
|
||||
# Normalize COBOL NOT-operators: X NOT = Y → X <> Y
|
||||
normalized = text
|
||||
@@ -292,11 +314,31 @@ def satisfying_value(field_info: dict, operator: str, value, want_true: bool) ->
|
||||
elif operator in ('<>', '!='):
|
||||
other = chr(65 + (ord(base_chr) - 64) % 26)
|
||||
return other.ljust(length, other)
|
||||
elif operator == '>':
|
||||
sv = str(value)[:length].ljust(length)
|
||||
chars = list(sv)
|
||||
last = chars[-1]
|
||||
if last not in '9Zz':
|
||||
chars[-1] = chr(ord(last) + 1)
|
||||
return ''.join(chars)
|
||||
elif operator == '<':
|
||||
sv = str(value)[:length].ljust(length)
|
||||
chars = list(sv)
|
||||
last = chars[-1]
|
||||
if last == ' ':
|
||||
pass
|
||||
elif last in '0Aa':
|
||||
chars[-1] = ' '
|
||||
else:
|
||||
chars[-1] = chr(ord(last) - 1)
|
||||
return ''.join(chars)
|
||||
else:
|
||||
if operator in ('=', '=='):
|
||||
other = chr(65 + (ord(base_chr) - 64) % 26)
|
||||
return other.ljust(length, other)
|
||||
elif operator in ('<>', '!='):
|
||||
return base_chr.ljust(length, base_chr)
|
||||
elif operator in ('>', '<'):
|
||||
return str(value)[:length].ljust(length)
|
||||
|
||||
return '0'.zfill(total)
|
||||
|
||||
Reference in New Issue
Block a user