Flow-Diagrams

From Webhuis wiki
Revision as of 23:00, 24 March 2026 by Martin (talk | contribs)
Jump to navigation Jump to search

Flow Diagrams

Document ID: PGCBL-FLD-001 · Version: 1.0 · Status: Approved · Last updated: 2026-03-17

Notation: Mermaid flowchart TD and sequenceDiagram

Data Dictionary | Primes system Design →



Contents

  1. Top-Level Application Flow
  2. primesgen Dispatch Flow
  3. Prime Generation — Sieve Loop
  4. Prime Report — Cursor Loop
  5. DAL (primes.cbl) Method Dispatch
  6. UI (primesui.cbl) Method Dispatch
  7. Inter-Program Call Sequence — Generate Run
  8. Inter-Program Call Sequence — Report Run



1. Top-Level Application Flow

Program: primesmain.cbl
What it shows: How primesmain reads the command-line argument, initialises the UI layer, routes to the correct processing path, and shuts down cleanly.

⚠️ The generate branch calls r92-generate-primes but the PERFORM inside that paragraph that drives the sieve is commented out. See Technical Specification — Defect T1.

flowchart TD
    A([START]) --> B[ACCEPT commandline-args\nFROM COMMAND-LINE]
    B --> C[MOVE commandline-args TO methods\nMOVE 'primesmain' TO program-name]
    C --> D[r90-start-session\nMOVE 'start' TO ui-methods\nCALL primesui USING primes-ui]

    D --> E{ui-method-ok?}
    E -->|No| F[DISPLAY emergency msg\nUPON scherm\nSTOP RUN]
    E -->|Yes| G[LOG 'UI initialisation succeeded.'\nLOG commandline-args]

    G --> H{EVALUATE\ncommandline-args}

    H -->|'report'| I[LOG 'Primes report generation starts.'\nMOVE 'report' TO methods]
    I --> J[r92-generate-primes\nCALL primesgen USING primes-session]

    H -->|'generate'| K[LOG 'Primes generation starts.'\nMOVE 'generate' TO methods]
    K --> L["⚠️ r92-generate-primes\n[PERFORM commented out in source]"]

    H -->|other| M[LOG 'Bad parameter'\nCALL primesui 'stop'\nSTOP RUN]

    J --> N[LOG 'Primes run complete, program stops.']
    L --> N
    N --> O[r99-stop-session\nMOVE 'stop' TO ui-methods\nCALL primesui USING primes-ui]
    O --> P([STOP RUN])

Key paragraphs:

Paragraph Action
r90-start-session Opens primes.prt; hard-stops if open fails
r92-generate-primes Single CALL to primesgen; passes primes-session by reference
r99-stop-session Flushes print buffer and closes primes.prt



2. primesgen Dispatch Flow

Program: primesgen.cbl
What it shows: How primesgen reads the methods field in primes-session and routes to either the generation or report path, then always performs cleanup.

flowchart TD
    A([ENTRY — primesgen\nUSING primes-session]) --> B{EVALUATE TRUE\non methods}

    B -->|report-primes| C[PERFORM r90-start-primes-report]
    B -->|generate-primes| D[PERFORM r91-start-primes-generation]
    B -->|other| E[LOG 'Bad method, primes process failed.']

    C --> F{session-method-ok?}
    F -->|Yes| G[PERFORM r86-report-primes\nUNTIL session-method-eof]
    F -->|No| Z

    D --> H{session-method-ok?}
    H -->|Yes| I[PERFORM r80-test-number\nUNTIL test-number = 999999999]
    H -->|No| Z

    G --> Z
    I --> Z
    E --> Z

    Z[r99-close-primes\nMOVE 'disconnect' TO dal-methods\nCALL primes USING primes-dal] --> END([EXIT PROGRAM])

3. Prime Generation — Sieve Loop

Program: primesgen.cbl (r80-series paragraphs) + primes.cbl (r81, r83)
What it shows: The trial-division sieve — how each candidate is tested, how composites are skipped, and how confirmed primes are persisted.

Algorithm in words: 1. Start at candidate = 3. Load first divisor (prime at ident 1 = prime 2, assumed pre-seeded). 2. Divide candidate by current divisor. 3. If remainder = 0 → composite. Add 2, reset divisor to first prime. 4. If divisor > √candidate → prime confirmed. Write to DB and print buffer. Add 2, reset divisor. 5. Otherwise → try next divisor (fetch next prime from DB by ident).

flowchart TD
    A([r91-start-primes-generation]) --> B[CALL primes 'connect']
    B --> C{dal-method-ok?}
    C -->|No| FAIL([session-result = 1\nEXIT])
    C -->|Yes| D[test-divider = 2\nold-ident = 1\ntest-number = 3\ntest-number-sqr = SQRT 3]
    D --> LOG1[LOG 'Database initialisation succeeded.']

    LOG1 --> LOOP{test-number\n= 999999999?}
    LOOP -->|Yes| CLOSE([r99-close-primes\nEXIT PROGRAM])

    LOOP -->|No| DIV[r80-test-number\nDIVIDE test-number BY test-divider\nGIVING test-quot REMAINDER test-rest]

    DIV --> EVAL{EVALUATE TRUE}

    EVAL -->|test-rest = 0\ncomposite| NEXT[r82-next-test-number]
    EVAL -->|test-divider > test-number-sqr\nprime confirmed| WRITE[r85-write-prime\nCALL primes 'write'\nINSERT INTO primes prime\nCALL primesui 'write'\nadd to print buffer]
    EVAL -->|otherwise\ntry next divisor| GETDIV[r89-get-next-divider\nCALL primes 'next-divider'\nSELECT prime WHERE ident = old-ident+1]

    WRITE --> NEXT
    GETDIV --> LOOP

    NEXT --> NA[ADD 2 TO test-number\nCOMPUTE test-number-sqr = test-number ** 0.5\nMOVE 1 TO old-ident\nr89-get-next-divider\nload first divisor]
    NA --> LOOP

Data flow through the sieve:

test-number (candidate)     ──→ DIVIDE ──→ test-rest
test-divider (from DB)      ──→ DIVIDE ──→ test-quot
test-number-sqr (SQRT)      ──→ compare to test-divider
old-ident (DB row pointer)  ──→ SELECT prime WHERE ident = old-ident+1 ──→ test-divider
prime-number (confirmed)    ──→ INSERT INTO primes (prime) ──→ database
                            ──→ u-number / u-sequence ──→ primesui print buffer

4. Prime Report — Cursor Loop

Program: primesgen.cbl (r86, r90, r94) + primes.cbl (s01, s02)
What it shows: How the report path opens a cursor over the database, fetches rows one at a time, and sends each to the print buffer.

flowchart TD
    A([r90-start-primes-report]) --> B[CALL primes 'connect'\ndal-methods = 'connect']
    B --> C{dal-method-ok?}
    C -->|No| FAIL1([LOG 'Database init failed.'\nsession-result = 1\nGOTO close])
    C -->|Yes| D[LOG 'Database initialisation succeeded.'\nCALL primes 'cursor'\ndal-methods = 'cursor']

    D --> E[s01-cursor:\nEXEC SQL START TRANSACTION\nEXEC SQL OPEN primescursor]
    E --> F{dal-method-ok?}
    F -->|No| FAIL2([LOG 'Cursor init failed.'\nsession-result = 1\nGOTO close])
    F -->|Yes| G[LOG 'Cursor initialisation succeeded.'\nr94-fetch → CALL primes 'next-prime'\nFETCH primescursor INTO primes-row]

    G --> H{dal-method-ok?}
    H -->|No| FAIL3([LOG 'Fetch first row nok.'\ndal-result = 1\nGOTO close])
    H -->|Yes| I[LOG 'Fetch first row ok.'\ndal-result = 0]

    I --> LOOP{session-method-eof?}
    LOOP -->|Yes| CLOSE[r99-close-primes\nCALL primes 'disconnect']
    CLOSE --> END([EXIT PROGRAM])

    LOOP -->|No| WRITE[r86-report-primes\nMOVE primes-data TO u-primes\nCALL primesui 'write']
    WRITE --> FETCH[r94-fetch\nCALL primes 'next-prime'\nFETCH primescursor INTO primes-row]
    FETCH --> FCHECK{dal-method-ok?}
    FCHECK -->|Yes| LOOP
    FCHECK -->|No| ERR[LOG 'Fetch failed.'\nsession-result = 1]
    ERR --> LOOP

⚠️ session-result = 1 (nok) is set on fetch failure, but the loop condition tests session-method-eof (value 9). These values do not match — the loop will continue rather than exit cleanly on cursor exhaustion. See Technical Specification — Defect T7.


5. DAL (primes.cbl) Method Dispatch

Program: primes.cbl
What it shows: The complete method-dispatch EVALUATE inside the DAL and the SQL operations each verb triggers. Each invocation handles exactly one operation.

flowchart TD
    A([ENTRY — primes\nUSING primes-dal]) --> B{EVALUATE TRUE\non dal-methods}

    B -->|db-connect| C["s00-connect\nEXEC SQL\n  CONNECT TO :DATASRC AS primes\n  USER :DBUSR USING :DBPWD"]
    B -->|db-cursor| D["s01-cursor\nEXEC SQL AT primes START TRANSACTION\nEXEC SQL OPEN primescursor"]
    B -->|next-prime| E["r80-get-next-prime → s02-fetch\nEXEC SQL FETCH primescursor\n  INTO :primes-row\nprimes-sequence = r-ident\nprime-number = r-prime"]
    B -->|next-divider| F["r81-get-next-divider\nnew-ident = old-ident + 1\nEXEC SQL AT primes\n  SELECT prime INTO :test-divider\n  FROM primes WHERE ident = :new-ident\nold-ident = new-ident"]
    B -->|write-prime| G["r83-write-prime\nEXEC SQL AT primes\n  INSERT INTO primes (prime)\n  VALUES (:prime)\n⚠️ COMMIT commented out"]
    B -->|db-disconnect| H["s99-disconnect\nEXEC SQL CONNECT RESET primes\n⚠️ cbsql.out shows 'primesdb'"]
    B -->|other| I[dal-result = 1]

    C --> SC{SQLCODE = 0?}
    SC -->|Yes| SOK[dal-result = 0\nLOG ok]
    SC -->|No| SNOK[dal-result = 1\nLOG error]

    D --> Z([EXIT PROGRAM])
    E --> Z
    F --> Z
    G --> Z
    H --> Z
    I --> Z
    SOK --> Z
    SNOK --> Z

Verb → SQL mapping (summary):

Verb SQL Notes
connect CONNECT TO :DATASRC AS primes … Sets connection alias primes
cursor START TRANSACTION + OPEN primescursor Report path only
next-prime FETCH primescursor INTO :primes-row Report path only
next-divider SELECT prime WHERE ident = :new-ident Generate path only
write INSERT INTO primes (prime) VALUES (:prime) Generate path only
disconnect CONNECT RESET primes Both paths



6. UI (primesui.cbl) Method Dispatch

Program: primesui.cbl
What it shows: How primesui routes each verb to the correct paragraph, and the detail of the write path — page management, line accumulation, and EOP handling.

flowchart TD
    A([ENTRY — primesui\nUSING primes-ui]) --> B{EVALUATE TRUE\non ui-methods}

    B -->|start-ui| C[r90-start-primesui\nOPEN OUTPUT fprinter\nprimes.prt]
    B -->|write-ui| D[r92-write-primesui]
    B -->|message-ui| E[r98-message-ui\nDISPLAY process-message\nUPON scherm]
    B -->|stop-ui| F[r99-stop-primesui]
    B -->|other| G[ui-method-result = 1]

    C --> C1{primes-prt-status\n= '00'?}
    C1 -->|Yes| C2[SET primes-idx TO 1\nui-method-result = 0\nLOG 'Open printer Ok']
    C1 -->|No| C3[ui-method-result = 1\nDISPLAY status on console]

    D --> D1[t-ident at primes-idx = u-sequence\nt-prime at primes-idx = u-number\nSET primes-idx UP BY 1]
    D1 --> D2{new-page?}
    D2 -->|Yes| D3[r93-new-page\nWRITE primes-heading\nWRITE table-header\nprint-new-page = 0]
    D2 -->|No| D4{primes-idx = 7?}
    D3 --> D4
    D4 -->|Yes| D5[WRITE primes-table line\n132-char data record\nSET primes-idx TO 1]
    D4 -->|No| D6{linage-counter = 53?}
    D5 --> D6
    D6 -->|Yes| D7[r94-eop\nWRITE blank line\nWRITE primes-footing\npage-number += 1\nprint-new-page = 1]
    D6 -->|No| Z

    F --> F1[MOVE 0 TO u-sequence and u-number\nPERFORM r92-write-primesui\nUNTIL new-page — flush partial line]
    F1 --> F2[LOG 'close printer'\nCLOSE fprinter\nui-method-result = primes-prt-status]

    C2 --> Z
    C3 --> Z
    D7 --> Z
    E --> Z
    F2 --> Z
    G --> Z
    Z([EXIT PROGRAM])

Page management state machine:

print-new-page = 1 (initial)
        │
        ▼ (on any "write" call)
  IF new-page → r93-new-page:
    WRITE heading + column header
    print-new-page = 0
        │
        ▼ (accumulate 6 entries)
  IF primes-idx = 7 → WRITE data line, reset idx to 1
        │
        ▼
  IF linage-counter = 53 → r94-eop:
    WRITE blank + footing
    page-number += 1
    print-new-page = 1  ← triggers new heading on next write

7. Inter-Program Call Sequence — Generate Run

What it shows: All CALL interactions between programs during a generate invocation, including the sieve loop, each divisor lookup, each prime insertion, and the shutdown sequence.

sequenceDiagram
    participant OS as OS / Shell
    participant main as primesmain
    participant gen as primesgen
    participant dal as primes (DAL)
    participant ui as primesui
    participant db as PostgreSQL

    OS->>main: execute with arg "generate"

    main->>ui: CALL "primesui" ui-methods="start"
    ui-->>main: ui-method-result=0 / primes.prt opened

    main->>ui: CALL "primesui" ui-methods="log-message" "Primes generation starts."
    ui->>OS: DISPLAY on console

    main->>gen: CALL "primesgen" methods="generate"

    gen->>dal: CALL "primes" dal-methods="connect"
    dal->>db: EXEC SQL CONNECT TO primes
    db-->>dal: SQLCODE=0
    dal-->>gen: dal-result=0

    gen->>ui: LOG "Database initialisation succeeded."

    loop sieve: test-number 3 → 999,999,999
        gen->>gen: DIVIDE test-number BY test-divider → test-rest

        alt test-rest = 0 (composite)
            gen->>gen: r82-next-test-number (test-number += 2, sqrt recalc)
            gen->>dal: CALL "primes" dal-methods="next-divider"
            dal->>db: SELECT prime WHERE ident = :new-ident
            db-->>dal: test-divider value
            dal-->>gen: dal-result=0

        else test-divider > √test-number (prime confirmed)
            gen->>ui: CALL "primesui" ui-methods="write" u-sequence, u-number
            ui->>ui: accumulate in 6-column buffer\nwrite line to primes.prt every 6 entries
            gen->>dal: CALL "primes" dal-methods="write"
            dal->>db: INSERT INTO primes (prime) VALUES (:prime)
            db-->>dal: SQLCODE=0
            dal-->>gen: dal-result=0
            gen->>gen: r82-next-test-number

        else otherwise (try next divisor)
            gen->>dal: CALL "primes" dal-methods="next-divider"
            dal->>db: SELECT prime WHERE ident = :new-ident
            db-->>dal: test-divider value
            dal-->>gen: dal-result=0
        end
    end

    gen->>dal: CALL "primes" dal-methods="disconnect"
    dal->>db: EXEC SQL CONNECT RESET primes
    db-->>dal: SQLCODE=0
    dal-->>gen: dal-result=0
    gen-->>main: session-result=0 / EXIT PROGRAM

    main->>ui: CALL "primesui" ui-methods="log-message" "Primes run complete."
    main->>ui: CALL "primesui" ui-methods="stop"
    ui->>ui: flush partial line\nCLOSE primes.prt
    ui-->>main: ui-method-result=0
    main->>OS: STOP RUN

8. Inter-Program Call Sequence — Report Run

What it shows: All CALL interactions during a report invocation — UI setup, database connection, transaction and cursor lifecycle, the fetch loop, and shutdown.

sequenceDiagram
    participant OS as OS / Shell
    participant main as primesmain
    participant gen as primesgen
    participant dal as primes (DAL)
    participant ui as primesui
    participant db as PostgreSQL
    participant prt as primes.prt

    OS->>main: execute with arg "report"

    main->>ui: CALL "primesui" ui-methods="start"
    ui->>prt: OPEN OUTPUT
    prt-->>ui: file-status="00"
    ui-->>main: ui-method-result=0

    main->>gen: CALL "primesgen" methods="report"

    gen->>dal: CALL "primes" dal-methods="connect"
    dal->>db: EXEC SQL CONNECT TO primes
    db-->>dal: SQLCODE=0
    dal-->>gen: dal-result=0
    gen->>ui: LOG "Database initialisation succeeded."

    gen->>dal: CALL "primes" dal-methods="cursor"
    dal->>db: EXEC SQL AT primes START TRANSACTION
    dal->>db: EXEC SQL OPEN primescursor
    db-->>dal: SQLCODE=0
    dal-->>gen: dal-result=0
    gen->>ui: LOG "Cursor initialisation succeeded."

    gen->>dal: r94-fetch → dal-methods="next-prime"
    dal->>db: EXEC SQL FETCH primescursor INTO :primes-row
    db-->>dal: first row (ident, prime)
    dal-->>gen: primes-sequence, prime-number / dal-result=0
    gen->>ui: LOG "Fetch first row ok."

    loop for each row until cursor exhausted
        gen->>ui: CALL "primesui" ui-methods="write" u-sequence / u-number
        ui->>prt: accumulate in 6-col buffer\nwrite 132-char line every 6 entries\nwrite heading+footing at page boundaries
        gen->>dal: r94-fetch → dal-methods="next-prime"
        dal->>db: EXEC SQL FETCH primescursor
        db-->>dal: next row or SQLCODE≠0 (no more rows)
        dal-->>gen: dal-result=0 (row) or dal-result=1 (eof/error)
        note over gen: ⚠️ session-method-eof (9) never set;\nloop exits on error (1), not clean eof
    end

    gen->>dal: CALL "primes" dal-methods="disconnect"
    dal->>db: EXEC SQL CONNECT RESET primes
    db-->>dal: SQLCODE=0
    dal-->>gen: dal-result=0
    gen-->>main: session-result=0 / EXIT PROGRAM

    main->>ui: CALL "primesui" ui-methods="log-message" "Primes run complete."
    main->>ui: CALL "primesui" ui-methods="stop"
    ui->>ui: pad remaining cells with zeros\nPERFORM r92 UNTIL new-page\nCLOSE fprinter
    ui->>prt: CLOSE
    prt-->>ui: file-status="00"
    ui-->>main: ui-method-result=0
    main->>OS: STOP RUN

Data Dictionary | Home →