"""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)