7a562c27a4
R4: core.py(289IF) + __init__.py(91IF) 内部関数全網羅
R4-design: design.py(161IF) enum_paths/constraint/redefines/occurs
R4-cond: cond.py(51IF) 全演算子×T/F×MC/DC
R4-coverage: coverage.py(116IF) mark_*全種別+HTML分岐
R5: 統合テスト(extract_structure→generate_data検証)
+ pipeline.py(34IF)+hina_agent.py(12IF)+read.py(54IF)
+ output.py(19IF)+orchestrator.py+classifier.py追加
R6: 複合ネストIF/PERFORM/EVAL/SEARCH+PIC解析全部
R7: FD方向解析+混乱グループ+contradiction+LLM応答
残環境依存: web/api(6IF), web/worker(6IF), runners/(6IF), gcov(6IF)
Co-Authored-By: Claude <noreply@anthropic.com>
1140 lines
50 KiB
Python
1140 lines
50 KiB
Python
"""R4: 深層カバレッジ — cobol_testgen/core.py 全関数の分岐網羅
|
|
|
|
ターゲット: core.py (289IF) + __init__.py (91IF) の内部関数
|
|
R3 では外部APIのみカバーしていたものを、内部関数の全分岐まで掘り下げる。
|
|
"""
|
|
import sys, os, tempfile, shutil, json, re
|
|
from pathlib import Path
|
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
|
|
|
|
P=0;F=0
|
|
def ck(v,m=""): global P,F; (P:=P+1) if v else (F:=F+1,print(f" FAIL {m}"))
|
|
def sec(n): print(f"\n--- {n} ---")
|
|
|
|
# ══════════════════════════════════════════════════════════════════
|
|
# 1. core.py — _BrParser 内部関数
|
|
# ══════════════════════════════════════════════════════════════════
|
|
sec("core._BrParser — parse_seq constructs")
|
|
|
|
from cobol_testgen.core import _BrParser, _basename, _init_child_names, _resolve_subscript, _apply_before_after
|
|
from cobol_testgen.core import trace_to_root, invert_through_chain, propagate_assignments, classify_field_roles
|
|
from cobol_testgen.core import scan_paragraphs, build_branch_tree
|
|
from cobol_testgen.models import BrIf, BrEval, BrSeq, BrPerform, BrSearch, Assign, CallNode, ExitNode, GoTo
|
|
|
|
# --- scan_paragraphs ---
|
|
p1 = scan_paragraphs(["MAIN.", "DISPLAY 'OK'.", "STOP RUN."])
|
|
ck("MAIN" in p1, "para basic")
|
|
p2 = scan_paragraphs(["PROCEDURE DIVISION."]); ck(len(p2)==0,"para no match")
|
|
p3 = scan_paragraphs(["IF .", "STOP RUN."]); ck(len(p3)==0,"para IF dot")
|
|
p4 = scan_paragraphs(["END-IF.", "STOP RUN."]); ck(len(p4)==0,"para scope ender")
|
|
p5 = scan_paragraphs(["S0 SECTION.","MAIN.","D 'OK'.","SUB.","D X."])
|
|
ck("S0" in p5 and "MAIN" in p5 and "SUB" in p5, "para section+multi")
|
|
|
|
# --- build_branch_tree ---
|
|
t1,a1 = build_branch_tree("PROCEDURE DIVISION.\nMAIN.\nSTOP RUN.\n",[])
|
|
ck(t1 is not None,"tree basic")
|
|
t2,a2 = build_branch_tree("MAIN.\nSTOP RUN.\n",[])
|
|
ck(t2 is not None,"tree no div")
|
|
t3,a3 = build_branch_tree("STOP RUN.",[])
|
|
ck(t3 is not None,"tree single line")
|
|
|
|
# --- IF ---
|
|
bp=_BrParser(["IF X>Y D 'A' ELSE D 'B'.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1 and isinstance(s.children[0],BrIf),"IF simple")
|
|
|
|
# IF compound condition
|
|
bp=_BrParser(["IF X>1 AND Y<5 D 'A' ELSE D 'B'.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"IF compound")
|
|
|
|
# ELSE IF multi-line
|
|
bp=_BrParser(["IF X=1","D 'A'","ELSE","IF X=2","D 'B'","ELSE","D 'C'","END-IF","END-IF.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"IF ELSE IF")
|
|
if len(s.children)>0:
|
|
ck(s.children[0].false_seq is not None and len(s.children[0].false_seq.children)>0,"IF ELSE IF false")
|
|
|
|
# EVALUATE
|
|
bp=_BrParser(["EVALUATE X WHEN 1 D 'A' WHEN 2 D 'B' WHEN OTHER D 'C' END-EVALUATE.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1 and isinstance(s.children[0],BrEval),"EVAL basic")
|
|
|
|
# EVALUATE ALSO
|
|
bp=_BrParser(["EVALUATE X ALSO Y WHEN 1 ALSO 2 D 'A' WHEN OTHER D 'B' END-EVALUATE.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1 and isinstance(s.children[0],BrEval),"EVAL ALSO")
|
|
if len(s.children)>0:
|
|
ck(s.children[0].subjects is not None and len(s.children[0].subjects)>=2,"EVAL ALSO subjects")
|
|
|
|
# PERFORM UNTIL
|
|
bp=_BrParser(["PERFORM UNTIL WS-EOF='Y' D 'A' END-PERFORM.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1 and isinstance(s.children[0],BrPerform),"PERF UNTIL")
|
|
|
|
# PERFORM TIMES
|
|
bp=_BrParser(["PERFORM 5 TIMES.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1 and isinstance(s.children[0],BrPerform),"PERF TIMES")
|
|
|
|
# PERFORM THRU
|
|
bp=_BrParser(["PERFORM A THRU B.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1 and isinstance(s.children[0],BrPerform),"PERF THRU")
|
|
|
|
# PERFORM para
|
|
bp=_BrParser(["PERFORM SUB.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1 and isinstance(s.children[0],BrPerform),"PERF para")
|
|
|
|
# PERFORM VARYING (single line)
|
|
bp=_BrParser(["PERFORM VARYING I FROM 1 BY 1 UNTIL I>10 D I END-PERFORM.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1 and isinstance(s.children[0],BrPerform),"PERF VARYING single")
|
|
|
|
# PERFORM VARYING (multi-line UNTIL)
|
|
bp=_BrParser(["PERFORM VARYING I FROM 1 BY 1","UNTIL I>10","D I","END-PERFORM.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"PERF VARYING multi")
|
|
|
|
# PERFORM VARYING para
|
|
bp=_BrParser(["PERFORM SUB VARYING I FROM 1 BY 1 UNTIL I>10.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"PERF VARYING para")
|
|
|
|
# PERFORM para UNTIL
|
|
bp=_BrParser(["PERFORM SUB UNTIL WS-EOF='Y'.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"PERF para UNTIL")
|
|
|
|
# PERFORM VARYING with FROM/BY on second line
|
|
bp=_BrParser(["PERFORM VARYING I","FROM 1 BY 1","UNTIL I>10","D I","END-PERFORM.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"PERF VARYING splitted")
|
|
|
|
# CALL
|
|
bp=_BrParser(["CALL 'SUB' USING BY REFERENCE WS-A BY CONTENT WS-B BY VALUE 100.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1 and isinstance(s.children[0],CallNode),"CALL")
|
|
if len(s.children)>0:
|
|
ck(len(s.children[0].using_params)>=3,"CALL params")
|
|
|
|
# SEARCH ALL
|
|
bp=_BrParser(["SEARCH ALL TBL WHEN KEY=100 D 'FOUND' END-SEARCH.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1 and isinstance(s.children[0],BrSearch),"SEARCH ALL")
|
|
|
|
# SEARCH with AT END + VARYING
|
|
bp=_BrParser(["SEARCH TBL VARYING IDX AT END D 'NF' WHEN KEY=100 D 'F' END-SEARCH.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1 and isinstance(s.children[0],BrSearch),"SEARCH VARYING")
|
|
|
|
# INITIALIZE
|
|
bp=_BrParser(["INITIALIZE WS-A WS-B REPLACING NUMERIC DATA BY 0 ALPHANUMERIC DATA BY SPACE.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"INITIALIZE")
|
|
|
|
# STRING (_parse_string returns BrSeq wrapping Assign)
|
|
bp=_BrParser(["STRING WS-A DELIMITED BY SIZE WS-B DELIMITED BY SPACE INTO WS-C","END-STRING","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"STRING")
|
|
|
|
# UNSTRING
|
|
bp=_BrParser(["UNSTRING WS-SRC INTO WS-A WS-B","END-UNSTRING","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"UNSTRING")
|
|
|
|
# INSPECT TALLYING
|
|
bp=_BrParser(["INSPECT WS-TXT TALLYING WS-CNT FOR LEADING 'A' BEFORE INITIAL 'B'.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"INSPECT tally")
|
|
|
|
# INSPECT REPLACING
|
|
bp=_BrParser(["INSPECT WS-TXT REPLACING ALL 'X' BY 'Y'.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"INSPECT replace")
|
|
|
|
# INSPECT CONVERTING
|
|
bp=_BrParser(["INSPECT WS-TXT CONVERTING 'ABC' TO 'XYZ'.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"INSPECT convert")
|
|
|
|
# READ INTO
|
|
bp=_BrParser(["READ F1 INTO WS-REC.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1 and isinstance(s.children[0],Assign),"READ INTO")
|
|
|
|
# WRITE FROM
|
|
bp=_BrParser(["WRITE REC FROM WS-DATA.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"WRITE FROM")
|
|
|
|
# WRITE bare
|
|
bp=_BrParser(["WRITE REC.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"WRITE bare")
|
|
|
|
# REWRITE bare
|
|
bp=_BrParser(["REWRITE REC.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"REWRITE bare")
|
|
|
|
# SET TO TRUE
|
|
bp=_BrParser(["SET WS-FLG TO TRUE.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"SET TRUE")
|
|
|
|
# SET TO FALSE
|
|
bp=_BrParser(["SET WS-FLG TO FALSE.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"SET FALSE")
|
|
|
|
# GO TO
|
|
bp=_BrParser(["GO TO EXIT-PARA.","STOP RUN."], paragraphs={"EXIT-PARA":(0,1)}, raw_lines=["EXIT-PARA.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1 and isinstance(s.children[0],GoTo),"GOTO")
|
|
|
|
# EXIT PARAGRAPH
|
|
bp=_BrParser(["EXIT PARAGRAPH.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1 and isinstance(s.children[0],ExitNode),"EXIT PARA")
|
|
|
|
# MOVE (variable to variable)
|
|
bp=_BrParser(["MOVE WS-SRC TO WS-TGT.","STOP RUN."], fields=[{"name":"WS-SRC"},{"name":"WS-TGT"}])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"MOVE var")
|
|
|
|
# MOVE (literal)
|
|
bp=_BrParser(["MOVE 'HELLO' TO WS-TXT.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"MOVE lit")
|
|
|
|
# COMPUTE (var op const)
|
|
bp=_BrParser(["COMPUTE X=Y+1.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"COMPUTE +")
|
|
|
|
# COMPUTE (const op var)
|
|
bp=_BrParser(["COMPUTE X=2*Y.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"COMPUTE *")
|
|
|
|
# COMPUTE (var op var)
|
|
bp=_BrParser(["COMPUTE X=A-B.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"COMPUTE - var")
|
|
|
|
# COMPUTE (complex)
|
|
bp=_BrParser(["COMPUTE X=(A+B)*C.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"COMPUTE complex")
|
|
|
|
# COMPUTE ROUNDED
|
|
bp=_BrParser(["COMPUTE X ROUNDED=Y/3.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"COMPUTE rounded")
|
|
|
|
# ADD (x TO y) literal
|
|
bp=_BrParser(["ADD 1 TO WS-CNT.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"ADD TO")
|
|
|
|
# ADD (variable TO y)
|
|
bp=_BrParser(["ADD WS-INC TO WS-CNT.","STOP RUN."], fields=[{"name":"WS-INC"},{"name":"WS-CNT"}])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"ADD VAR TO")
|
|
|
|
# ADD (x TO y GIVING z) literal
|
|
bp=_BrParser(["ADD 1 TO WS-CNT GIVING WS-RES.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"ADD TO GIVING")
|
|
|
|
# ADD variable TO y GIVING z
|
|
bp=_BrParser(["ADD WS-A TO WS-B GIVING WS-C.","STOP RUN."], fields=[{"name":"WS-A"},{"name":"WS-B"}])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"ADD VAR TO GIVING")
|
|
|
|
# ADD (GIVING multi) literal
|
|
bp=_BrParser(["ADD 1 2 3 GIVING WS-TOTAL.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"ADD GIVING multi lit")
|
|
|
|
# ADD (GIVING multi) mixed
|
|
bp=_BrParser(["ADD WS-A WS-B 1 GIVING WS-C.","STOP RUN."], fields=[{"name":"WS-A"},{"name":"WS-B"}])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"ADD GIVING mixed")
|
|
|
|
# ADD (GIVING multi) all fields
|
|
bp=_BrParser(["ADD WS-A WS-B GIVING WS-C.","STOP RUN."], fields=[{"name":"WS-A"},{"name":"WS-B"},{"name":"WS-C"}])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"ADD GIVING fields")
|
|
|
|
# SUBTRACT (x FROM y) literal
|
|
bp=_BrParser(["SUBTRACT 1 FROM WS-CNT.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"SUBTRACT FROM")
|
|
|
|
# SUBTRACT (x FROM y GIVING z)
|
|
bp=_BrParser(["SUBTRACT 1 FROM WS-CNT GIVING WS-RES.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"SUB FROM GIVING")
|
|
|
|
# SUBTRACT variable FROM y GIVING z
|
|
bp=_BrParser(["SUBTRACT WS-A FROM WS-B GIVING WS-C.","STOP RUN."], fields=[{"name":"WS-A"},{"name":"WS-B"}])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"SUB VAR FROM GIVING")
|
|
|
|
# MULTIPLY (x BY y)
|
|
bp=_BrParser(["MULTIPLY 2 BY WS-CNT.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"MULTIPLY BY")
|
|
|
|
# MULTIPLY (a BY b GIVING z) literal
|
|
bp=_BrParser(["MULTIPLY 3 BY WS-CNT GIVING WS-RES.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"MULT BY GIVING lit")
|
|
|
|
# MULTIPLY var BY var GIVING z
|
|
bp=_BrParser(["MULTIPLY WS-A BY WS-B GIVING WS-C.","STOP RUN."], fields=[{"name":"WS-A"},{"name":"WS-B"}])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"MULT VAR BY GIVING")
|
|
|
|
# DIVIDE (x INTO y)
|
|
bp=_BrParser(["DIVIDE 2 INTO WS-NUM.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"DIVIDE INTO")
|
|
|
|
# DIVIDE (a INTO b GIVING z) literal
|
|
bp=_BrParser(["DIVIDE 10 INTO WS-NUM GIVING WS-RES.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"DIVIDE INTO GIVING")
|
|
|
|
# DIVIDE (a INTO b GIVING z REMAINDER r) literal → returns BrSeq as 1 child
|
|
bp=_BrParser(["DIVIDE 10 INTO WS-NUM GIVING WS-Q REMAINDER WS-R.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"DIVIDE INTO GIVING REM")
|
|
|
|
# DIVIDE var INTO var GIVING z
|
|
bp=_BrParser(["DIVIDE WS-A INTO WS-B GIVING WS-C.","STOP RUN."], fields=[{"name":"WS-A"},{"name":"WS-B"}])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"DIVIDE VAR INTO GIVING")
|
|
|
|
# DIVIDE var INTO var GIVING z REMAINDER r → BrSeq as 1 child
|
|
bp=_BrParser(["DIVIDE WS-A INTO WS-B GIVING WS-Q REMAINDER WS-R.","STOP RUN."], fields=[{"name":"WS-A"},{"name":"WS-B"}])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"DIVIDE VAR INTO GIVING REM")
|
|
|
|
# DIVIDE a BY b GIVING z
|
|
bp=_BrParser(["DIVIDE WS-A BY WS-B GIVING WS-C.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"DIVIDE BY GIVING")
|
|
|
|
# DIVIDE a BY b GIVING z REMAINDER r → BrSeq as 1 child
|
|
bp=_BrParser(["DIVIDE WS-A BY WS-B GIVING WS-Q REMAINDER WS-R.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"DIVIDE BY GIVING REM")
|
|
|
|
# ACCEPT (DATE/TIME/DAY)
|
|
bp=_BrParser(["ACCEPT WS-D FROM DATE.","ACCEPT WS-T FROM TIME.","ACCEPT WS-Y FROM DAY.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=3,"ACCEPT DATE/TIME/DAY")
|
|
|
|
# ACCEPT DAY-OF-WEEK / YEAR / HHMMSS / YYYYMMDD
|
|
bp=_BrParser(["ACCEPT WS-D FROM DAY-OF-WEEK.","ACCEPT WS-Y FROM YEAR.","ACCEPT WS-H FROM HHMMSS.","ACCEPT WS-YMD FROM YYYYMMDD.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=4,"ACCEPT DAY-OF-WEEK/YEAR/HHMMSS/YYYYMMDD")
|
|
|
|
# ACCEPT bare
|
|
bp=_BrParser(["ACCEPT WS-X.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"ACCEPT bare")
|
|
|
|
# IF with THEN next line
|
|
bp=_BrParser(["IF X>1","THEN","D 'A'.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"IF THEN next")
|
|
|
|
# IF with multi-line condition
|
|
bp=_BrParser(["IF X>1 AND","Y<5","D 'A'.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"IF multi-line cond")
|
|
|
|
|
|
# ══════════════════════════════════════════════════════════════════
|
|
# 2. propagate_assignments — 全パス (1〜8) + 境界値
|
|
# ══════════════════════════════════════════════════════════════════
|
|
sec("propagate_assignments")
|
|
|
|
_f = [
|
|
{"name":"WS-A","pic_info":{"type":"numeric","digits":5,"decimal":0,"length":5,"signed":False}},
|
|
{"name":"WS-B","pic_info":{"type":"numeric","digits":5,"decimal":0,"length":5,"signed":False}},
|
|
{"name":"WS-C","pic_info":{"type":"numeric","digits":5,"decimal":0,"length":5,"signed":False}},
|
|
{"name":"WS-X","pic_info":{"type":"alphanumeric","length":10,"digits":0,"decimal":0,"signed":False}},
|
|
{"name":"WS-Y","pic_info":{"type":"alphabetic","length":5,"digits":0,"decimal":0,"signed":False}},
|
|
{"name":"WS-D","pic_info":{"type":"numeric","digits":8,"decimal":2,"length":10,"signed":True}},
|
|
{"name":"WS-FLG","pic_info":{"type":"alphanumeric","length":1,"digits":0,"decimal":0,"signed":False}},
|
|
]
|
|
|
|
# Pass 1: variable-to-variable MOVE
|
|
r={"WS-SRC":"100","WS-A":""}; propagate_assignments(r,{"WS-A":[{"type":"move","source_vars":["WS-SRC"]}]},_f)
|
|
ck(r.get("WS-A")==r.get("WS-SRC"),"p1 var move")
|
|
|
|
# Pass 2: literal MOVE numeric → zero-padded to 5 digits
|
|
r={"WS-A":""}; propagate_assignments(r,{"WS-A":[{"type":"move_literal","literal":"123"}]},_f)
|
|
ck(r.get("WS-A")=="00123","p2 lit num")
|
|
|
|
# Pass 2: literal MOVE alphanumeric → padded to 10
|
|
r={"WS-X":""}; propagate_assignments(r,{"WS-X":[{"type":"move_literal","literal":"HELLO"}]},_f)
|
|
ck(r.get("WS-X")=="HELLO ","p2 lit alpha")
|
|
|
|
# Pass 3: INITIALIZE (numeric → 00000)
|
|
r={"WS-A":"999"}; propagate_assignments(r,{"WS-A":[{"type":"initialize"}]},_f)
|
|
ck(r.get("WS-A")=="00000","p3 init num")
|
|
|
|
# Pass 3: INITIALIZE (alphanumeric → spaces)
|
|
r={"WS-X":"OLD"}; propagate_assignments(r,{"WS-X":[{"type":"initialize"}]},_f)
|
|
ck(" " in str(r.get("WS-X","")),"p3 init alpha")
|
|
|
|
# Pass 3: INITIALIZE with REPLACING matched
|
|
r={"WS-A":""}; propagate_assignments(r,{"WS-A":[{"type":"initialize","replacing":{"NUMERIC":"500"}}]},_f)
|
|
ck(r.get("WS-A")=="00500","p3 init repl num")
|
|
|
|
# Pass 3: INITIALIZE with REPLACING unmatched type (alpha but repl says NUMERIC)
|
|
r={"WS-X":""}; propagate_assignments(r,{"WS-X":[{"type":"initialize","replacing":{"NUMERIC":"100"}}]},_f)
|
|
ck(" " in str(r.get("WS-X","")),"p3 init repl mismatch")
|
|
|
|
# Pass 3.5: READ INTO
|
|
r={"FD-REC":"ABC","WS-REC":""}; propagate_assignments(r,{"WS-REC":[{"type":"read_into","file":"F1"}]},_f,file_sec={"F1":["FD-REC"]})
|
|
ck(r.get("WS-REC") is not None,"p3.5 read into")
|
|
|
|
# Pass 4: COMPUTE +
|
|
r={"WS-A":"00010"}; propagate_assignments(r,{"WS-A":[{"type":"compute","source_vars":["WS-A"],"op":"+","const":5}]},_f)
|
|
ck(r.get("WS-A")=="00015","p4 compute +")
|
|
|
|
# COMPUTE -
|
|
r={"WS-A":"00020"}; propagate_assignments(r,{"WS-A":[{"type":"compute","source_vars":["WS-A"],"op":"-","const":5}]},_f)
|
|
ck(r.get("WS-A")=="00015","p4 compute -")
|
|
|
|
# COMPUTE *
|
|
r={"WS-A":"00003"}; propagate_assignments(r,{"WS-A":[{"type":"compute","source_vars":["WS-A"],"op":"*","const":4}]},_f)
|
|
ck(r.get("WS-A")=="00012","p4 compute *")
|
|
|
|
# COMPUTE /
|
|
r={"WS-A":"00100"}; propagate_assignments(r,{"WS-A":[{"type":"compute","source_vars":["WS-A"],"op":"/","const":3}]},_f)
|
|
ck(r.get("WS-A")=="00033","p4 compute /")
|
|
|
|
# COMPUTE rem
|
|
r={"WS-A":"00010"}; propagate_assignments(r,{"WS-A":[{"type":"compute","source_vars":["WS-A"],"op":"rem","const":3}]},_f)
|
|
ck(r.get("WS-A")=="00001","p4 compute rem")
|
|
|
|
# COMPUTE 2 vars +
|
|
r={"WS-A":"00010","WS-B":"00005"}; propagate_assignments(r,{"WS-D":[{"type":"compute","source_vars":["WS-A","WS-B"],"op":"+"}]},_f)
|
|
ck(r.get("WS-D") is not None,"p4 compute 2var +")
|
|
|
|
# COMPUTE 2 vars -
|
|
r={"WS-A":"00010","WS-B":"00003"}; propagate_assignments(r,{"WS-D":[{"type":"compute","source_vars":["WS-A","WS-B"],"op":"-"}]},_f)
|
|
ck(r.get("WS-D") is not None,"p4 compute 2var -")
|
|
|
|
# COMPUTE 2 vars /
|
|
r={"WS-A":"00006","WS-B":"00003"}; propagate_assignments(r,{"WS-D":[{"type":"compute","source_vars":["WS-A","WS-B"],"op":"/"}]},_f)
|
|
ck(r.get("WS-D") is not None,"p4 compute 2var /")
|
|
|
|
# COMPUTE 3+ vars +
|
|
r={"WS-A":"001","WS-B":"002","WS-C":"003"}; propagate_assignments(r,{"WS-D":[{"type":"compute","source_vars":["WS-A","WS-B","WS-C"],"op":"+"}]},_f)
|
|
ck(r.get("WS-D") is not None,"p4 compute 3var +")
|
|
|
|
# INSPECT TALLYING LEADING
|
|
r={"WS-X":"AAABBB","WS-CNT":""}; propagate_assignments(r,{"WS-CNT":[{"type":"inspect","tgt":"WS-X","source_vars":["WS-X"],
|
|
"sub_ops":[("tally",{"count_var":"WS-CNT","kind":"LEADING","char":"A","before_after":"","delimiter":""})]}]},_f)
|
|
ck(r.get("WS-CNT") is not None,"p4.5 tally LEADING")
|
|
|
|
# TALLYING TRAILING
|
|
r={"WS-X":"BBBAAA","WS-CNT":""}; propagate_assignments(r,{"WS-CNT":[{"type":"inspect","tgt":"WS-X","source_vars":["WS-X"],
|
|
"sub_ops":[("tally",{"count_var":"WS-CNT","kind":"TRAILING","char":"A","before_after":"","delimiter":""})]}]},_f)
|
|
ck(r.get("WS-CNT") is not None,"p4.5 tally TRAILING")
|
|
|
|
# TALLYING CHARACTERS
|
|
r={"WS-X":"ABCDEF","WS-CNT":""}; propagate_assignments(r,{"WS-CNT":[{"type":"inspect","tgt":"WS-X","source_vars":["WS-X"],
|
|
"sub_ops":[("tally",{"count_var":"WS-CNT","kind":"CHARACTERS","char":"","before_after":"","delimiter":""})]}]},_f)
|
|
ck(r.get("WS-CNT") is not None,"p4.5 tally CHARACTERS")
|
|
|
|
# REPLACING ALL
|
|
r={"WS-X":"HELLO WORLD"}; propagate_assignments(r,{"WS-X":[{"type":"inspect","tgt":"WS-X","source_vars":["WS-X"],
|
|
"sub_ops":[("replace",{"kind":"ALL","src":"L","dst":"X","before_after":"","delimiter":""})]}]},_f)
|
|
ck("X" in r.get("WS-X",""),"p4.5 replace ALL")
|
|
|
|
# REPLACING LEADING
|
|
r={"WS-X":"AAABBB"}; propagate_assignments(r,{"WS-X":[{"type":"inspect","tgt":"WS-X","source_vars":["WS-X"],
|
|
"sub_ops":[("replace",{"kind":"LEADING","src":"A","dst":"X","before_after":"","delimiter":""})]}]},_f)
|
|
ck("X" in r.get("WS-X",""),"p4.5 replace LEADING")
|
|
|
|
# REPLACING FIRST
|
|
r={"WS-X":"ABABAB"}; propagate_assignments(r,{"WS-X":[{"type":"inspect","tgt":"WS-X","source_vars":["WS-X"],
|
|
"sub_ops":[("replace",{"kind":"FIRST","src":"A","dst":"X","before_after":"","delimiter":""})]}]},_f)
|
|
ck("X" in r.get("WS-X",""),"p4.5 replace FIRST")
|
|
|
|
# REPLACING CHARACTERS (else)
|
|
r={"WS-X":"TEST"}; propagate_assignments(r,{"WS-X":[{"type":"inspect","tgt":"WS-X","source_vars":["WS-X"],
|
|
"sub_ops":[("replace",{"kind":"CHARACTERS","src":"A","dst":"X","before_after":"","delimiter":""})]}]},_f)
|
|
ck(r.get("WS-X","") is not None,"p4.5 replace CHARACTERS")
|
|
|
|
# CONVERTING
|
|
r={"WS-X":"ABC"}; propagate_assignments(r,{"WS-X":[{"type":"inspect","tgt":"WS-X","source_vars":["WS-X"],
|
|
"sub_ops":[("convert",{"from_chars":"ABC","to_chars":"XYZ","before_after":"","delimiter":""})]}]},_f)
|
|
ck(r.get("WS-X")=="XYZ","p4.5 convert")
|
|
|
|
# INSPECT tally with BEFORE
|
|
r={"WS-X":"XXXYYY","WS-CNT":""}; propagate_assignments(r,{"WS-CNT":[{"type":"inspect","tgt":"WS-X","source_vars":["WS-X"],
|
|
"sub_ops":[("tally",{"count_var":"WS-CNT","kind":"LEADING","char":"X","before_after":"BEFORE","delimiter":"Y"})]}]},_f)
|
|
ck(r.get("WS-CNT") is not None,"p4.5 tally BEFORE")
|
|
|
|
# INSPECT replace with AFTER
|
|
r={"WS-X":"PRE--DATA--POST"}; propagate_assignments(r,{"WS-X":[{"type":"inspect","tgt":"WS-X","source_vars":["WS-X"],
|
|
"sub_ops":[("replace",{"kind":"ALL","src":"-","dst":"_","before_after":"AFTER","delimiter":"--"})]}]},_f)
|
|
ck(r.get("WS-X") is not None,"p4.5 replace AFTER")
|
|
|
|
# Pass 5: STRING concat
|
|
r={"WS-A":"HELLO","WS-B":"WORLD","WS-X":""}; propagate_assignments(r,{"WS-X":[{"type":"string_concat","source_vars":["WS-A","WS-B"]}]},_f)
|
|
ck(r.get("WS-X")=="HELLOWORLD","p5 string")
|
|
|
|
# Pass 5: UNSTRING (index 0)
|
|
r={"WS-X":"DATA","WS-A":""}; propagate_assignments(r,{"WS-A":[{"type":"unstring_split","source_vars":["WS-X"],"index":0}]},_f)
|
|
ck(r.get("WS-A")=="DATA","p5 unstring idx0")
|
|
|
|
# Pass 5: UNSTRING (index > 0)
|
|
r={"WS-X":"DATA","WS-B":""}; propagate_assignments(r,{"WS-B":[{"type":"unstring_split","source_vars":["WS-X"],"index":1}]},_f)
|
|
ck(r.get("WS-B") is not None,"p5 unstring idx1")
|
|
|
|
# Pass 6: WRITE FROM (with proper levels)
|
|
_f_fd=[{"name":"REC","level":5,"pic_info":{"type":"group","length":10}},
|
|
{"name":"REC-A","level":10,"pic_info":{"type":"alphanumeric","length":5}},
|
|
{"name":"REC-B","level":10,"pic_info":{"type":"alphanumeric","length":5}}]
|
|
r={"WS-BUF":"AAAAABBBBB"}; propagate_assignments(r,{"WS-BUF":[{"type":"write_from","file":"REC","source_vars":["WS-BUF"]}]},_f_fd)
|
|
ck("REC-A" in r or "REC-B" in r,"p6 write from")
|
|
|
|
# Pass 6: READ INTO (second pass lines)
|
|
r={"FD-REC":"XYZ","WS-REC":""}; propagate_assignments(r,{"WS-REC":[{"type":"read_into","file":"F1"}]},_f,file_sec={"F1":["FD-REC"]})
|
|
ck(r.get("WS-REC") is not None,"p6 read into 2")
|
|
|
|
# Pass 7: ACCEPT FROM DATE (alphanumeric)
|
|
r={"WS-D":""}; propagate_assignments(r,{"WS-D":[{"type":"accept","from":"DATE"}]},_f)
|
|
ck(len(str(r.get("WS-D","")))>0,"p7 accept DATE")
|
|
|
|
# Pass 7: ACCEPT FROM TIME
|
|
r={"WS-D":""}; propagate_assignments(r,{"WS-D":[{"type":"accept","from":"TIME"}]},_f)
|
|
ck(len(str(r.get("WS-D","")))>0,"p7 accept TIME")
|
|
|
|
# Pass 7: ACCEPT FROM DAY
|
|
r={"WS-D":""}; propagate_assignments(r,{"WS-D":[{"type":"accept","from":"DAY"}]},_f)
|
|
ck(len(str(r.get("WS-D","")))>0,"p7 accept DAY")
|
|
|
|
# Pass 7: ACCEPT DAY-OF-WEEK (numeric → zfill total=10)
|
|
r={"WS-D":""}; propagate_assignments(r,{"WS-D":[{"type":"accept","from":"DAY-OF-WEEK"}]},_f)
|
|
ck(r.get("WS-D")=="0000000003","p7 accept DAY-OF-WEEK")
|
|
|
|
# Pass 7: ACCEPT YEAR (numeric → zfill)
|
|
r={"WS-D":""}; propagate_assignments(r,{"WS-D":[{"type":"accept","from":"YEAR"}]},_f)
|
|
ck(r.get("WS-D")=="0000002026","p7 accept YEAR")
|
|
|
|
# Pass 7: ACCEPT numeric DATE
|
|
r={"WS-A":""}; propagate_assignments(r,{"WS-A":[{"type":"accept","from":"DATE"}]},_f)
|
|
ck(len(str(r.get("WS-A","")))>0,"p7 accept DATE numeric")
|
|
|
|
# Pass 8: SET TRUE
|
|
r={"WS-FLG":""}; propagate_assignments(r,{"WS-FLG":[{"type":"set_true","88_name":"FLG-88","value":"Y"}]},_f)
|
|
ck(r.get("WS-FLG") is not None,"p8 set true")
|
|
|
|
# SET TRUE alpha
|
|
r={"WS-X":""}; propagate_assignments(r,{"WS-X":[{"type":"set_true","88_name":"X-88","value":"Y"}]},_f)
|
|
ck(r.get("WS-X") is not None,"p8 set true alpha")
|
|
|
|
# Figurative constants
|
|
r={"WS-A":"","WS-X":""}; propagate_assignments(r,{"WS-A":[{"type":"move_literal","literal":"ZERO"}],"WS-X":[{"type":"move_literal","literal":"SPACE"}]},_f)
|
|
ck(r.get("WS-A") is not None and r.get("WS-X") is not None,"fig ZERO+SPACE")
|
|
|
|
# HIGH-VALUE numeric
|
|
r={"WS-A":""}; propagate_assignments(r,{"WS-A":[{"type":"move_literal","literal":"HIGH-VALUE"}]},_f)
|
|
ck(r.get("WS-A") is not None,"fig HIGH-VALUE")
|
|
|
|
# LOW-VALUE alpha
|
|
r={"WS-X":""}; propagate_assignments(r,{"WS-X":[{"type":"move_literal","literal":"LOW-VALUE"}]},_f)
|
|
ck(r.get("WS-X") is not None,"fig LOW-VALUE")
|
|
|
|
# Unknown type INITIALIZE
|
|
_unk=[{"name":"WS-Z","pic_info":{"type":"unknown","length":0}}]
|
|
r={"WS-Z":"X"}; propagate_assignments(r,{"WS-Z":[{"type":"initialize"}]},_unk)
|
|
ck(r.get("WS-Z") is not None,"init unknown")
|
|
|
|
# Dict-style assignment
|
|
r={"WS-A":""}; propagate_assignments(r,{"WS-A":{"type":"move_literal","literal":"999"}},_f)
|
|
ck(r.get("WS-A")=="00999","dict assign")
|
|
|
|
# Self-ref unanchored compute (should converge after iter 0)
|
|
r={"WS-A":""}; propagate_assignments(r,{"WS-A":[{"type":"compute","source_vars":["WS-A"],"op":"+","const":1}]},_f)
|
|
ck(r.get("WS-A") is not None,"self-ref unanchored")
|
|
|
|
# Anchored compute (not skipped)
|
|
r={"WS-A":"10"}; propagate_assignments(r,{"WS-A":[{"type":"move_literal","literal":"10"},{"type":"compute","source_vars":["WS-A"],"op":"+","const":5}]},_f)
|
|
ck(int(str(r.get("WS-A","0")))>=10,"anchored compute")
|
|
|
|
|
|
# ══════════════════════════════════════════════════════════════════
|
|
# 3. classify_field_roles
|
|
# ══════════════════════════════════════════════════════════════════
|
|
sec("classify_field_roles")
|
|
|
|
# Basic branch-tree roles
|
|
t=BrSeq(); t.add(BrIf("WS-A>0"))
|
|
t.children[0].cond_tree=type('obj',(object,),{"field":"WS-A","op":">","value":"0","is_true":True})()
|
|
t.children[0].true_seq=BrSeq(); t.children[0].true_seq.add(Assign("WS-B",{"type":"move_literal","literal":"100","source_vars":[]}))
|
|
rf=[{"name":"WS-A"},{"name":"WS-B"},{"name":"WS-C"}]
|
|
r=classify_field_roles(t,{},rf)
|
|
ck("WS-A" in r and "WS-B" in r,"basic roles")
|
|
|
|
# LINKAGE defaults to input
|
|
r=classify_field_roles(BrSeq(),{},[{"name":"P","section":"LINKAGE"},{"name":"W","section":"WORKING-STORAGE"}])
|
|
ck("P" in r or "W" in r,"LINKAGE default")
|
|
|
|
# CALL reference (read+write)
|
|
t=BrSeq(); t.add(CallNode("SUB",using_params=[{"name":"P","mechanism":"reference"}]))
|
|
r=classify_field_roles(t,{},[{"name":"P","section":"LINKAGE"}])
|
|
ck("P" in r,"CALL ref")
|
|
|
|
# CALL content (read only)
|
|
t=BrSeq(); t.add(CallNode("SUB",using_params=[{"name":"P","mechanism":"content"}]))
|
|
r=classify_field_roles(t,{},[{"name":"P","section":"LINKAGE"}])
|
|
ck("P" in r,"CALL content")
|
|
|
|
# ACCEPT/DISPLAY
|
|
r=classify_field_roles(BrSeq(),{},[{"name":"WS-INP"},{"name":"WS-OUT"}],proc_text="ACCEPT WS-INP. DISPLAY WS-OUT.")
|
|
ck("WS-INP" in r and "WS-OUT" in r,"ACCEPT/DISPLAY")
|
|
|
|
# EVALUATE subject
|
|
t=BrSeq(); en=BrEval("WS-A"); en.when_list=[("1",BrSeq())]; en.cond_trees=[None]; en.other_seq=BrSeq(); t.add(en)
|
|
r=classify_field_roles(t,{},[{"name":"WS-A"}])
|
|
ck("WS-A" in r,"EVAL subject")
|
|
|
|
# read_into
|
|
t=BrSeq(); t.add(Assign("WS-R",{"type":"read_into","source_vars":[],"file":"F1"}))
|
|
r=classify_field_roles(t,{},[{"name":"WS-R"}])
|
|
ck("WS-R" in r,"read_into")
|
|
|
|
# PERFORM condition+varying
|
|
t=BrSeq(); pn=BrPerform("until",condition="WS-A>0"); pn.varying_var="WS-I"; pn.body_seq=BrSeq(); t.add(pn)
|
|
r=classify_field_roles(t,{},[{"name":"WS-A"},{"name":"WS-I"}])
|
|
ck("WS-A" in r and "WS-I" in r,"PERF var")
|
|
|
|
# Initialize (child names)
|
|
t=BrSeq(); t.add(Assign("GRP",{"type":"initialize","source_vars":[]}))
|
|
r=classify_field_roles(t,{},[{"name":"GRP"},{"name":"GRP-A"}])
|
|
ck("GRP" in r or "GRP-A" in r,"init grp")
|
|
|
|
|
|
# ══════════════════════════════════════════════════════════════════
|
|
# 4. trace_to_root
|
|
# ══════════════════════════════════════════════════════════════════
|
|
sec("trace_to_root")
|
|
|
|
v,c=trace_to_root("X",{"X":[{"type":"move_literal","literal":"100","source_vars":[]}]},[])
|
|
ck(v is not None and len(c)>=1,"trace simple")
|
|
|
|
v,c=trace_to_root("X",{"X":[{"type":"move","source_vars":["Y"]}],"Y":[{"type":"move","source_vars":["Z"]}],"Z":[{"type":"move_literal","literal":"100","source_vars":[]}]},[])
|
|
ck(len(c)>=2,"trace multi-hop")
|
|
|
|
v,c=trace_to_root("X",{"X":[{"type":"move","source_vars":["X"]}]},[])
|
|
ck(v is not None,"trace self-ref")
|
|
|
|
v,c=trace_to_root("X",{"X":[{"type":"compute","source_vars":["Y"],"op":"+","const":1}],"Y":[{"type":"move_literal","literal":"100","source_vars":[]}]},[])
|
|
ck(len(c)>=1,"trace adder")
|
|
|
|
v,c=trace_to_root("X",{"X":[{"type":"compute","source_vars":["Y","Z"],"op":"+"}],"Y":[{"type":"move_literal","literal":"100","source_vars":[]}]},[])
|
|
ck(len(c)>=1,"trace multi-source")
|
|
|
|
v,c=trace_to_root("X",{"X":[{"type":"move_literal","literal":"100"}]},[],path_assign={"X":[{"type":"move_literal","literal":"200"}]})
|
|
ck(len(c)>=1,"trace path_assign")
|
|
|
|
v,c=trace_to_root("X",{"X":[{"type":"move","source_vars":["X"]},{"type":"move_literal","literal":"100","source_vars":[]}]},[])
|
|
ck(len(c)>=1,"trace skip selfref")
|
|
|
|
v,c=trace_to_root("X",{"X":[{"type":"move","source_vars":["Y"]}]},[])
|
|
ck(len(c)==1,"trace missing src")
|
|
|
|
v,c=trace_to_root("X",{},[]); ck(c==[],"trace empty")
|
|
|
|
|
|
# ══════════════════════════════════════════════════════════════════
|
|
# 5. invert_through_chain
|
|
# ══════════════════════════════════════════════════════════════════
|
|
sec("invert_through_chain")
|
|
|
|
v,_,_=invert_through_chain("X",[("X",{"type":"move","source_vars":["Y"]})],">","100"); ck(v is not None,"inv move")
|
|
v,o,_=invert_through_chain("X",[("X",{"type":"compute","op":"+","const":5,"source_vars":["Y"]})],">","100"); ck(o is not None,"inv +")
|
|
v,_,_=invert_through_chain("X",[("X",{"type":"compute","op":"-","const":5,"source_vars":["Y"]})],">","100"); ck(v is not None,"inv -")
|
|
v,_,_=invert_through_chain("X",[("X",{"type":"compute","op":"*","const":2,"source_vars":["Y"]})],">","100"); ck(v is not None,"inv *")
|
|
v,_,_=invert_through_chain("X",[("X",{"type":"compute","op":"/","const":2,"source_vars":["Y"]})],">","100"); ck(v is not None,"inv /")
|
|
v,_,_=invert_through_chain("X",[("X",{"type":"compute","op":"/","const":0,"source_vars":["Y"]})],">","100"); ck(v is not None,"inv div0")
|
|
v,_,_=invert_through_chain("X",[("X",{"type":"compute","op":"+","const":None,"source_vars":["Y","Z"]})],">","100")
|
|
ck(v is not None,"inv multi")
|
|
v,_,_=invert_through_chain("X",[("X",{"type":"move","source_vars":["Y"]})],">","ABC"); ck(v is not None,"inv non-num")
|
|
v,o,_=invert_through_chain("X",[("X",{"type":"compute","op":"/","const":3,"source_vars":["Y"]})],">","10"); ck(v is not None,"inv float")
|
|
|
|
|
|
# ══════════════════════════════════════════════════════════════════
|
|
# 6. 補助関数
|
|
# ══════════════════════════════════════════════════════════════════
|
|
sec("Helpers")
|
|
|
|
ck(_basename("WS-TABLE(1)")=="WS-TABLE","base subscript")
|
|
ck(_basename("WS-X")=="WS-X","base no sub")
|
|
ck(_basename("")=="","base empty")
|
|
|
|
# _init_child_names
|
|
fg=[{"name":"GRP","level":5,"pic_info":{"type":"group"}},
|
|
{"name":"SUB","level":10,"pic_info":{"type":"unknown"}},
|
|
{"name":"A","level":15,"pic_info":{"type":"numeric","digits":3}},
|
|
{"name":"B","level":15,"pic_info":{"type":"alphanumeric","length":5}},
|
|
{"name":"B-88","level":15,"is_88":True},
|
|
{"name":"C","level":15,"redefines":"B"},
|
|
{"name":"D","level":77}]
|
|
c=_init_child_names("GRP",fg); ck(len(c)>=1,"init children")
|
|
ck("A" in c or "B" in c,"init recursive")
|
|
|
|
# _resolve_subscript
|
|
ck(_resolve_subscript("X(IDX)",{"IDX":3})=="X(3)","resolve num")
|
|
ck(eval("_resolve_subscript('X(IDX)',{'IDX':'VAL'})")=="X(IDX)","resolve non-num")
|
|
ck(_resolve_subscript("X",{})=="X","resolve no paren")
|
|
ck(_resolve_subscript("WS-TBL(WS-IDX)",{"WS-IDX":5})=="WS-TBL(5)","resolve real")
|
|
|
|
# _apply_before_after
|
|
ck(_apply_before_after("ABCDEF","BEFORE","CD")=="AB","before")
|
|
ck(_apply_before_after("ABCDEF","AFTER","CD")=="EF","after")
|
|
ck(_apply_before_after("ABCDEF","BEFORE","NONE")=="ABCDEF","before no match")
|
|
ck(_apply_before_after("ABCDEF","","")=="ABCDEF","empty")
|
|
ck(_apply_before_after("ABCDEF","UNKNOWN","X")=="ABCDEF","unknown")
|
|
|
|
# _expand_figurative
|
|
ck(_BrParser._expand_figurative("ZERO")=="0","fig ZERO")
|
|
ck(_BrParser._expand_figurative("SPACE")==" ","fig SPACE")
|
|
ck(_BrParser._expand_figurative("OTHER")=="OTHER","fig OTHER")
|
|
|
|
# _parse_inspect_phrase via instance
|
|
bp_ip=_BrParser([])
|
|
p0=bp_ip._parse_inspect_phrase("TALLYING CNT FOR LEADING 'A' BEFORE INITIAL 'B'")
|
|
ck(p0 is not None and p0[0]=="tally","phrase tally")
|
|
p1=bp_ip._parse_inspect_phrase("REPLACING ALL 'X' BY 'Y' AFTER INITIAL 'Z'")
|
|
ck(p1 is not None and p1[0]=="replace","phrase replace")
|
|
p2=bp_ip._parse_inspect_phrase("CONVERTING 'ABC' TO 'XYZ'")
|
|
ck(p2 is not None and p2[0]=="convert","phrase convert")
|
|
p3=bp_ip._parse_inspect_phrase("UNKNOWN")
|
|
ck(p3 is None,"phrase unknown")
|
|
|
|
|
|
# ══════════════════════════════════════════════════════════════════
|
|
# 7. _BrParser._parse_if 詳細
|
|
# ══════════════════════════════════════════════════════════════════
|
|
sec("_parse_if edge cases")
|
|
|
|
bp=_BrParser(["IF X=1 D 'A' ELSE IF X=2 D 'B' ELSE D 'C'.","STOP RUN."])
|
|
if1=bp._parse_if()
|
|
ck(if1 is not None and if1.true_seq is not None,"parse_if ELSE IF")
|
|
|
|
# IF with THEN next line
|
|
bp=_BrParser(["IF X>1","THEN","D 'A'.","END-IF.","STOP RUN."])
|
|
if2=bp._parse_if(); ck(if2 is not None,"parse_if THEN line")
|
|
|
|
# IF multi-line cond
|
|
bp=_BrParser(["IF X>1","AND Y<5","D 'A'.","STOP RUN."])
|
|
if3=bp._parse_if(); ck(if3 is not None,"parse_if multi cond")
|
|
|
|
|
|
# ══════════════════════════════════════════════════════════════════
|
|
# 8. expand_occurs 詳細
|
|
# ══════════════════════════════════════════════════════════════════
|
|
sec("expand_occurs")
|
|
from cobol_testgen import expand_occurs, _add_subscript
|
|
|
|
ck(_add_subscript("WS-CELL",1)=="WS-CELL(1)","add_sub 1")
|
|
ck(_add_subscript("WS-CELL(1)",2)=="WS-CELL(1,2)","add_sub multi")
|
|
|
|
# with children
|
|
eo=expand_occurs([{"name":"T","level":5,"occurs":3,"is_88":False,"occurs_depending":None},
|
|
{"name":"E","level":10,"pic":"X","occurs":0,"is_88":False}])
|
|
ck(len(eo)>=3,"occurs children")
|
|
|
|
# without children
|
|
eo=expand_occurs([{"name":"T","level":5,"occurs":2,"is_88":False,"occurs_depending":None}])
|
|
ck(len(eo)>=2,"occurs no child")
|
|
|
|
# with 88-level
|
|
eo=expand_occurs([{"name":"T","level":5,"occurs":2,"is_88":False,"occurs_depending":None},
|
|
{"name":"V","level":10,"pic":"X","occurs":0,"is_88":True}])
|
|
ck(len(eo)>=2,"occurs 88")
|
|
|
|
# nested occurs (child also has occurs)
|
|
eo=expand_occurs([{"name":"T","level":5,"occurs":2,"is_88":False,"occurs_depending":None},
|
|
{"name":"S","level":10,"occurs":3,"is_88":False,"occurs_depending":None}])
|
|
ck(len(eo)>=2,"occurs nested")
|
|
|
|
# 77-level break
|
|
eo=expand_occurs([{"name":"T","level":5,"occurs":2,"is_88":False,"occurs_depending":None},
|
|
{"name":"X","level":77,"occurs":0,"is_88":False}])
|
|
ck(len(eo)>=2,"occurs 77-break")
|
|
|
|
# recursive
|
|
eo=expand_occurs([{"name":"T","level":5,"occurs":2,"is_88":False,"occurs_depending":None},
|
|
{"name":"S","level":10,"occurs":0,"is_88":False,"pic":"X"}])
|
|
ck(len(eo)>=3,"occurs recursive")
|
|
|
|
|
|
# ══════════════════════════════════════════════════════════════════
|
|
# 9. extract_structure — 内部関数群
|
|
# ══════════════════════════════════════════════════════════════════
|
|
sec("extract_structure internals")
|
|
from cobol_testgen import extract_structure
|
|
|
|
es=extract_structure(" IDENTIFICATION DIVISION. PROGRAM-ID. T. DATA DIVISION. WORKING-STORAGE SECTION. 01 A PIC 9. PROCEDURE DIVISION. IF A>1 D 'Y' ELSE D 'N'. STOP RUN.")
|
|
ck(es.get("total_branches") is not None,"es basic")
|
|
|
|
_ML = "\n".join # shorthand for multi-line COBOL source
|
|
|
|
es=extract_structure(_ML([
|
|
" IDENTIFICATION DIVISION.",
|
|
" PROGRAM-ID. T.",
|
|
" DATA DIVISION.",
|
|
" WORKING-STORAGE SECTION.",
|
|
" 01 X PIC 9.",
|
|
" PROCEDURE DIVISION.",
|
|
" EVALUATE X",
|
|
" WHEN 1 DISPLAY 'A'",
|
|
" WHEN 2 DISPLAY 'B'",
|
|
" WHEN OTHER DISPLAY 'C'",
|
|
" END-EVALUATE.",
|
|
" STOP RUN."]))
|
|
ck(es.get("has_evaluate")==True,"es eval")
|
|
|
|
es=extract_structure(_ML([
|
|
" IDENTIFICATION DIVISION.",
|
|
" PROGRAM-ID. T.",
|
|
" DATA DIVISION.",
|
|
" WORKING-STORAGE SECTION.",
|
|
" 01 A PIC 9.",
|
|
" PROCEDURE DIVISION.",
|
|
" CALL 'SUB' USING A.",
|
|
" STOP RUN."]))
|
|
ck(es.get("has_call")==True,"es call")
|
|
|
|
es=extract_structure(_ML([
|
|
" IDENTIFICATION DIVISION.",
|
|
" PROGRAM-ID. T.",
|
|
" DATA DIVISION.",
|
|
" WORKING-STORAGE SECTION.",
|
|
" 01 X PIC 9.",
|
|
" PROCEDURE DIVISION.",
|
|
" DIVIDE 100 INTO X.",
|
|
" STOP RUN."]))
|
|
ck(100.0 in es.get("divide_constants",[]),"es divide")
|
|
|
|
es=extract_structure(_ML([
|
|
" IDENTIFICATION DIVISION.",
|
|
" PROGRAM-ID. T.",
|
|
" DATA DIVISION.",
|
|
" WORKING-STORAGE SECTION.",
|
|
" 01 X PIC X(10).",
|
|
" PROCEDURE DIVISION.",
|
|
" INSPECT X TALLYING CNT FOR CHARACTERS.",
|
|
" STOP RUN."]))
|
|
ck(es.get("has_inspect") is not None,"es inspect")
|
|
|
|
es=extract_structure(_ML([
|
|
" IDENTIFICATION DIVISION.",
|
|
" PROGRAM-ID. T.",
|
|
" DATA DIVISION.",
|
|
" WORKING-STORAGE SECTION.",
|
|
" 01 X PIC X(10).",
|
|
" 01 Y PIC X(10).",
|
|
" PROCEDURE DIVISION.",
|
|
" STRING X INTO Y END-STRING.",
|
|
" STOP RUN."]))
|
|
ck(es.get("has_string") is not None,"es string")
|
|
|
|
es=extract_structure(_ML([
|
|
" IDENTIFICATION DIVISION.",
|
|
" PROGRAM-ID. T.",
|
|
" DATA DIVISION.",
|
|
" WORKING-STORAGE SECTION.",
|
|
" 01 WS-KEY PIC 9.",
|
|
" 01 WS-PREV-KEY PIC 9.",
|
|
" PROCEDURE DIVISION.",
|
|
" IF WS-KEY = WS-PREV-KEY DISPLAY 'SAME'.",
|
|
" STOP RUN."]))
|
|
ck(es.get("total_branches")>=1,"es key")
|
|
|
|
es=extract_structure(_ML([
|
|
" IDENTIFICATION DIVISION.",
|
|
" PROGRAM-ID. T.",
|
|
" DATA DIVISION.",
|
|
" WORKING-STORAGE SECTION.",
|
|
" 01 X PIC 9.",
|
|
" PROCEDURE DIVISION.",
|
|
" PERFORM 5 TIMES",
|
|
" DISPLAY 'A'",
|
|
" END-PERFORM.",
|
|
" STOP RUN."]))
|
|
ck(len(es.get("perform_patterns",[]))>=1,"es perf")
|
|
|
|
es=extract_structure(" IDENTIFICATION DIVISION.\n PROGRAM-ID. T.")
|
|
ck(es.get("total_branches")==0,"es no proc")
|
|
|
|
es=extract_structure("")
|
|
ck(es.get("file_count") is not None,"es empty")
|
|
|
|
# Compound IF
|
|
es=extract_structure(_ML([
|
|
" IDENTIFICATION DIVISION.",
|
|
" PROGRAM-ID. T.",
|
|
" DATA DIVISION.",
|
|
" WORKING-STORAGE SECTION.",
|
|
" 01 A PIC 9.",
|
|
" 01 B PIC 9.",
|
|
" PROCEDURE DIVISION.",
|
|
" IF A > 1 AND B < 5 DISPLAY 'Y' ELSE DISPLAY 'N'.",
|
|
" STOP RUN."]))
|
|
ck(es.get("if_types",{}).get("compound",0)>=1,"es compound")
|
|
|
|
# Equality IF
|
|
es=extract_structure(_ML([
|
|
" IDENTIFICATION DIVISION.",
|
|
" PROGRAM-ID. T.",
|
|
" DATA DIVISION.",
|
|
" WORKING-STORAGE SECTION.",
|
|
" 01 A PIC 9.",
|
|
" PROCEDURE DIVISION.",
|
|
" IF A = 1 DISPLAY 'Y'.",
|
|
" STOP RUN."]))
|
|
ck(es.get("if_types",{}).get("equality",0)>=1,"es equality")
|
|
|
|
# Comparison IF
|
|
es=extract_structure(_ML([
|
|
" IDENTIFICATION DIVISION.",
|
|
" PROGRAM-ID. T.",
|
|
" DATA DIVISION.",
|
|
" WORKING-STORAGE SECTION.",
|
|
" 01 A PIC 9.",
|
|
" PROCEDURE DIVISION.",
|
|
" IF A > 5 DISPLAY 'Y'.",
|
|
" STOP RUN."]))
|
|
ck(es.get("if_types",{}).get("comparison",0)>=1,"es comparison")
|
|
|
|
# Nested IF
|
|
es=extract_structure(_ML([
|
|
" IDENTIFICATION DIVISION.",
|
|
" PROGRAM-ID. T.",
|
|
" DATA DIVISION.",
|
|
" WORKING-STORAGE SECTION.",
|
|
" 01 A PIC 9.",
|
|
" 01 B PIC 9.",
|
|
" PROCEDURE DIVISION.",
|
|
" IF A > 0",
|
|
" IF B > 0 DISPLAY 'Y'",
|
|
" ELSE DISPLAY 'N'.",
|
|
" STOP RUN."]))
|
|
ck(es.get("if_types",{}).get("nested_depth",0)>=1,"es nested")
|
|
|
|
# Variable patterns
|
|
es=extract_structure(_ML([
|
|
" IDENTIFICATION DIVISION.",
|
|
" PROGRAM-ID. T.",
|
|
" DATA DIVISION.",
|
|
" WORKING-STORAGE SECTION.",
|
|
" 01 WS-PREV-KEY PIC 9.",
|
|
" 01 WS-CNT PIC 9.",
|
|
" 01 WS-ERR PIC X.",
|
|
" 01 WS-SW PIC X.",
|
|
" 01 WS-IDX PIC 9.",
|
|
" 01 WS-SAVE-KEY PIC X.",
|
|
" 01 WS-WK PIC X."]))
|
|
ck(len(es.get("variable_patterns",{}))>0,"es var patterns")
|
|
|
|
# Main loop with PERFORM + READ (needs proper COBOL structure, FILE-CONTROL before DATA DIVISION)
|
|
es=extract_structure(_ML([
|
|
" IDENTIFICATION DIVISION.",
|
|
" PROGRAM-ID. T.",
|
|
" DATA DIVISION.",
|
|
" WORKING-STORAGE SECTION.",
|
|
" 01 WS-EOF PIC X.",
|
|
" 01 WS-KEY PIC X.",
|
|
" PROCEDURE DIVISION.",
|
|
" PERFORM UNTIL WS-EOF = 'Y'",
|
|
" READ FILE1 INTO WS-KEY",
|
|
" END-PERFORM.",
|
|
" STOP RUN."]))
|
|
ck(es.get("main_loop") is not None or es.get("perform_patterns") is not None,"es main loop")
|
|
|
|
# OPEN/CLOSE pattern (proper multi-line)
|
|
es=extract_structure(_ML([
|
|
" IDENTIFICATION DIVISION.",
|
|
" PROGRAM-ID. T.",
|
|
" DATA DIVISION.",
|
|
" WORKING-STORAGE SECTION.",
|
|
" 01 X PIC 9.",
|
|
" PROCEDURE DIVISION.",
|
|
" OPEN INPUT F1.",
|
|
" CLOSE F1."]))
|
|
ck(es.get("open_pattern") in ("sequential","open-close-open"),"es open pattern")
|
|
|
|
# FILLER
|
|
es=extract_structure(_ML([
|
|
" IDENTIFICATION DIVISION.",
|
|
" PROGRAM-ID. T.",
|
|
" DATA DIVISION.",
|
|
" WORKING-STORAGE SECTION.",
|
|
" 01 X PIC 9.",
|
|
" 01 FILLER PIC X(10)."]))
|
|
ck(es.get("file_count")>=0,"es filler")
|
|
|
|
|
|
# ══════════════════════════════════════════════════════════════════
|
|
# 10. incremental_supplement
|
|
# ══════════════════════════════════════════════════════════════════
|
|
sec("incremental_supplement")
|
|
from cobol_testgen import incremental_supplement
|
|
|
|
t=BrSeq(); t.add(BrIf("X>0"))
|
|
s=incremental_supplement(t,[1]); ck(len(s)>=1,"incr basic")
|
|
s=incremental_supplement(t,[]); ck(len(s)==0,"incr empty")
|
|
s=incremental_supplement(t,[999]); ck(len(s)==0,"incr gap miss")
|
|
|
|
t2=BrSeq()
|
|
en=BrEval("X"); en.when_list=[("1",BrSeq())]; en.cond_trees=[None]; en.other_seq=BrSeq(); t2.add(en)
|
|
s=incremental_supplement(t2,[1]); ck(len(s)>=1,"incr eval")
|
|
|
|
pn=BrPerform("until",condition="X>0"); pn.body_seq=BrSeq()
|
|
t2.add(pn)
|
|
s=incremental_supplement(t2,[1]); ck(s is not None,"incr perform")
|
|
|
|
|
|
# ══════════════════════════════════════════════════════════════════
|
|
# 11. generate_data
|
|
# ══════════════════════════════════════════════════════════════════
|
|
sec("generate_data")
|
|
from cobol_testgen import generate_data
|
|
|
|
gd=generate_data(" IDENTIFICATION DIVISION. PROGRAM-ID. T. DATA DIVISION. WORKING-STORAGE SECTION. 01 X PIC 9."); ck(len(gd)==0,"gd no proc")
|
|
gd=generate_data(" IDENTIFICATION DIVISION. PROGRAM-ID. T. DATA DIVISION. WORKING-STORAGE SECTION. 01 A PIC 99. PROCEDURE DIVISION. IF A>50 D 'Y' ELSE D 'N'. STOP RUN.")
|
|
ck(len(gd)>=1,"gd simple")
|
|
gd=generate_data(" IDENTIFICATION DIVISION. PROGRAM-ID. T. DATA DIVISION. WORKING-STORAGE SECTION. 01 X PIC 9. PROCEDURE DIVISION. STOP RUN.",structure={"branch_tree_obj":BrSeq()})
|
|
ck(len(gd)>=0,"gd struct")
|
|
|
|
|
|
# ══════════════════════════════════════════════════════════════════
|
|
# 12. _parse_compute_expr 全パターン
|
|
# ══════════════════════════════════════════════════════════════════
|
|
sec("_parse_compute_expr patterns")
|
|
px=_BrParser._parse_compute_expr
|
|
ck(px(None,"X","2*Y") is not None,"pexpr const*var")
|
|
ck(px(None,"X","Y+1") is not None,"pexpr var+const")
|
|
ck(px(None,"X","A-B") is not None,"pexpr var-var")
|
|
ck(px(None,"X","(A+B)*C-D") is not None,"pexpr complex")
|
|
ck(px(None,"X","") is not None,"pexpr empty")
|
|
|
|
|
|
# ══════════════════════════════════════════════════════════════════
|
|
# 13. 境界値ケース: _BrParser エッジ
|
|
# ══════════════════════════════════════════════════════════════════
|
|
sec("boundary cases")
|
|
|
|
# Empty parse_seq
|
|
bp=_BrParser([]); s=bp.parse_seq(); ck(len(s.children)==0,"empty parse")
|
|
|
|
# Unrecognized line (just advances)
|
|
bp=_BrParser(["UNKNOWN STMT.","STOP RUN."]); s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(bp.pos==2,"unknown line")
|
|
|
|
# compute with missing expr (peek next line)
|
|
bp=_BrParser(["COMPUTE X =","Y+1","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"compute multi-line")
|
|
|
|
# ADD GIVING mixed with field and literal
|
|
bp=_BrParser(["ADD 1 2 3 GIVING WS-TOTAL.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"ADD GIVING all lit")
|
|
|
|
# DIVIDE BY GIVING (not INTO)
|
|
bp=_BrParser(["DIVIDE A BY B GIVING C.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"DIVIDE BY GIVING")
|
|
|
|
# DIVIDE BY GIVING REMAINDER → BrSeq as 1 child
|
|
bp=_BrParser(["DIVIDE A BY B GIVING C REMAINDER D.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"DIVIDE BY GIVING REM")
|
|
|
|
# MOVE with subscript
|
|
bp=_BrParser(["MOVE 100 TO WS-TBL(WS-IDX).","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"MOVE subscript")
|
|
|
|
# MOVE with subscript source
|
|
bp=_BrParser(["MOVE WS-SRC TO WS-TGT(1).","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"MOVE subscript tgt")
|
|
|
|
# ADD variable TO y with unknown var → falls through
|
|
bp=_BrParser(["ADD UNKNOWN TO WS-X.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=0,"ADD unknown")
|
|
|
|
# COMPUTE with continuation on next line
|
|
bp=_BrParser(["COMPUTE X ROUNDED =", "Y + 1", "STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"COMPUTE multi expr")
|
|
|
|
# GO TO with body
|
|
bp=_BrParser(["GO TO SUB.","STOP RUN."], paragraphs={"SUB":(0,1)}, raw_lines=["SUB.","D 'OK'."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"GOTO with body")
|
|
|
|
# _is_end with end_check
|
|
bp=_BrParser(["WHEN X>0 D 'A'.","STOP RUN."])
|
|
s=bp.parse_seq(end_check=lambda l: l.startswith("WHEN"))
|
|
ck(len(s.children)==0,"is_end custom")
|
|
|
|
# EVALUATE with AND/OR continuation
|
|
bp=_BrParser(["EVALUATE X","WHEN 1","AND 2","D 'A'","END-EVALUATE.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"EVAL AND cont")
|
|
|
|
# CALL with empty params
|
|
bp=_BrParser(["CALL 'SUB'.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=0,"CALL no params")
|
|
|
|
# CALL with malformed line
|
|
bp=_BrParser(["CALL","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=0,"CALL malformed")
|
|
|
|
# SET with unknown 88-level
|
|
bp=_BrParser(["SET WS-UNKNOWN TO TRUE.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"SET unknown 88 true")
|
|
|
|
bp=_BrParser(["SET WS-UNKNOWN TO FALSE.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=1,"SET unknown 88 false")
|
|
|
|
# MULTIPLY with unknown var (no field match) → fall through
|
|
bp=_BrParser(["MULTIPLY UNKNOWN BY WS-X.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=0,"MULT unknown")
|
|
|
|
# ADD string literal (not numeric)
|
|
bp=_BrParser(["ADD 'ABC' TO WS-X.","STOP RUN."])
|
|
s=bp.parse_seq(terminators={"STOP RUN"})
|
|
ck(len(s.children)>=0,"ADD string")
|
|
|
|
|
|
print(f"\n{'='*55}\nR4: {P} PASS / {F} FAIL\n{'='*55}")
|
|
if F > 0: sys.exit(1)
|