GitNexus/docs/code-indexing/cobol/regex-extraction.md
Gergo Magyar 4af677e637 feat: add COBOL language support with regex extraction pipeline
Standalone COBOL processor following the markdown-processor.ts pattern:
- No LanguageProvider modification — COBOL uses regex, not tree-sitter
- No SupportedLanguages enum change — standalone processor pattern

New files:
- cobol-processor.ts — orchestrator (processCobol, isCobolFile, isJclFile)
- cobol/cobol-preprocessor.ts — regex state machine extraction (~888 LOC)
- cobol/cobol-copy-expander.ts — COPY statement expansion with circular detection
- cobol/jcl-parser.ts — JCL job/step/DD extraction
- cobol/jcl-processor.ts — JCL graph node creation

Extraction produces:
- Module nodes (PROGRAM-ID)
- Function nodes (paragraphs)
- Namespace nodes (sections)
- Property nodes (data items)
- CALLS edges (PERFORM intra-file, CALL cross-program)
- IMPORTS edges (COPY statements)
- CONTAINS edges (section → paragraph hierarchy)

Pipeline integration: single processCobol() call in Phase 2.6

54 new tests (33 COBOL + 21 JCL), all 3889 tests pass.
2026-03-24 14:39:25 +00:00

7.9 KiB

COBOL Regex Extraction

The extractCobolSymbolsWithRegex() function in cobol-preprocessor.ts performs single-pass, state-machine-driven extraction of all COBOL symbols. This document describes the state machine, line processing flow, and every regex pattern used.

State Machine: Division Tracking

The extractor tracks which COBOL division is currently being processed. Division transitions are detected by the RE_DIVISION pattern.

stateDiagram-v2
    [*] --> null : Start of file
    null --> identification : IDENTIFICATION DIVISION
    identification --> environment : ENVIRONMENT DIVISION
    environment --> data : DATA DIVISION
    data --> procedure : PROCEDURE DIVISION

    note right of identification
        Extracts: PROGRAM-ID, AUTHOR, DATE-WRITTEN
    end note
    note right of environment
        Extracts: SELECT ... ASSIGN ... (file declarations)
    end note
    note right of data
        Extracts: FD entries, data items (01-77, 88), COPY
    end note
    note right of procedure
        Extracts: paragraphs, sections, PERFORM, CALL,
        ENTRY, MOVE, EXEC SQL/CICS
    end note

State Machine: Data Section Tracking

Within the DATA DIVISION, a secondary state machine tracks the current section to tag data items with their origin.

stateDiagram-v2
    [*] --> unknown : DATA DIVISION entered
    unknown --> working_storage : WORKING-STORAGE SECTION
    unknown --> linkage : LINKAGE SECTION
    unknown --> file : FILE SECTION
    unknown --> local_storage : LOCAL-STORAGE SECTION
    working_storage --> linkage : LINKAGE SECTION
    working_storage --> file : FILE SECTION
    linkage --> working_storage : WORKING-STORAGE SECTION
    file --> working_storage : WORKING-STORAGE SECTION
    file --> linkage : LINKAGE SECTION
    local_storage --> working_storage : WORKING-STORAGE SECTION

Within the ENVIRONMENT DIVISION, the currentEnvSection tracks whether we are in INPUT-OUTPUT or CONFIGURATION section. SELECT statement accumulation only occurs in INPUT-OUTPUT.

Line Processing Flow

Each raw source line goes through this pipeline:

Raw line
  |
  v
Length < 7? ---------> Skip (flush pending if any)
  |
  v
Indicator col 7
  |
  +-- '*' or '/' -----> Comment: skip entirely
  |
  +-- '-' ------------> Continuation: append to pending line
  |
  +-- other ----------> Normal: flush pending, strip inline comments (|),
                        buffer as new pending logical line

After all lines are processed, the final pending line is flushed, along with any accumulated SELECT statement.

Inline Comment Stripping

Enterprise COBOL (particularly Italian dialect) uses the pipe character | as an inline comment marker. Everything from | to end of line is stripped before processing.

Patch Marker Handling

The preprocessCobolSource() function (run before extraction in the worker) replaces non-standard content in columns 1-6. Standard COBOL expects spaces or digit sequence numbers in this area. If any letter or # character is found, the entire sequence area is replaced with 6 spaces:

Before: mzADD MOVE WK-AMT TO WK-TOTAL
After:        MOVE WK-AMT TO WK-TOTAL

This preserves exact line count for position mapping.

Regex Pattern Reference

All patterns are compiled once as module-level constants and reused across calls.

Division and Section Detection

Constant Pattern Purpose Example Match
RE_DIVISION \b(IDENTIFICATION|ENVIRONMENT|DATA|PROCEDURE)\s+DIVISION\b Division boundary PROCEDURE DIVISION
RE_SECTION \b(WORKING-STORAGE|LINKAGE|FILE|LOCAL-STORAGE|INPUT-OUTPUT|CONFIGURATION)\s+SECTION\b Section boundary WORKING-STORAGE SECTION

IDENTIFICATION DIVISION

Constant Pattern Purpose Example Match
RE_PROGRAM_ID \bPROGRAM-ID\.\s*([A-Z][A-Z0-9-]*) Program name PROGRAM-ID. BGTABFL
RE_AUTHOR ^\s+AUTHOR\.\s*(.+) Author metadata AUTHOR. D. Smith
RE_DATE_WRITTEN ^\s+DATE-WRITTEN\.\s*(.+) Date metadata DATE-WRITTEN. 2024-01-15

ENVIRONMENT DIVISION

Constant Pattern Purpose Example Match
RE_SELECT_START \bSELECT\s+([A-Z][A-Z0-9-]+) File SELECT start SELECT MASTER-FILE

SELECT statements are accumulated across multiple lines until a period terminator is found, then parsed for ASSIGN, ORGANIZATION, ACCESS, RECORD KEY, and FILE STATUS clauses.

DATA DIVISION

Constant Pattern Purpose Example Match
RE_FD ^\s+FD\s+([A-Z][A-Z0-9-]+) File description FD MASTER-FILE
RE_DATA_ITEM ^\s+(\d{1,2})\s+([A-Z][A-Z0-9-]+)\s*(.*) Data item (01-77) 05 WK-NAME PIC X(30)
RE_ANONYMOUS_REDEFINES ^\s+(\d{1,2})\s+REDEFINES\s+([A-Z][A-Z0-9-]+) Anonymous REDEFINES 01 REDEFINES WK-REC
RE_88_LEVEL ^\s+88\s+([A-Z][A-Z0-9-]+)\s+VALUES?\s+(?:ARE\s+)?(.+) Condition name 88 WK-ACTIVE VALUE "Y"

The trailing clauses of RE_DATA_ITEM are parsed by parseDataItemClauses() for PIC, USAGE, OCCURS, and REDEFINES.

PROCEDURE DIVISION

Constant Pattern Purpose Example Match
RE_PROC_SECTION ^ ([A-Z][A-Z0-9-]+)\s+SECTION\.\s*$ Procedure section header MAIN-LOGIC SECTION.
RE_PROC_PARAGRAPH ^ ([A-Z][A-Z0-9-]+)\.\s*$ Paragraph header PROCESS-RECORD.
RE_PERFORM \bPERFORM\s+([A-Z][A-Z0-9-]+)(?:\s+THRU\s+([A-Z][A-Z0-9-]+))? PERFORM call PERFORM CALC-TAX THRU CALC-TAX-EXIT
RE_PROC_USING \bPROCEDURE\s+DIVISION\s+USING\s+([\s\S]*?)(?:\.|$) USING parameters PROCEDURE DIVISION USING WK-PARAM
RE_ENTRY \bENTRY\s+"([^"]+)"(?:\s+USING\s+([\s\S]*?))?(?:\.|$) ENTRY point ENTRY "SUBPROG" USING WK-DATA
RE_MOVE \bMOVE\s+(CORRESPONDING\s+)?([A-Z][A-Z0-9-]+)\s+TO\s+([A-Z][A-Z0-9-]+) MOVE statement MOVE WK-NAME TO OUT-NAME

Note: RE_PROC_SECTION and RE_PROC_PARAGRAPH require exactly 7 spaces of leading indentation (COBOL area A starting at column 8). This is the standard COBOL paragraph indentation.

All-Division Patterns

These patterns are checked regardless of current division:

Constant Pattern Purpose Example Match
RE_CALL \bCALL\s+"([^"]+)" External program call CALL "BGTABUP"
RE_COPY_UNQUOTED \bCOPY\s+([A-Z][A-Z0-9-]+)(?:\s|\.) COPY (unquoted) COPY CPSESP.
RE_COPY_QUOTED \bCOPY\s+"([^"]+)"(?:\s|\.) COPY (quoted) COPY "WORKGRID.CPY".

EXEC Block Patterns

Constant Pattern Purpose Example Match
RE_EXEC_SQL_START \bEXEC\s+SQL\b Start of EXEC SQL block EXEC SQL
RE_EXEC_CICS_START \bEXEC\s+CICS\b Start of EXEC CICS block EXEC CICS
RE_END_EXEC \bEND-EXEC\b End of EXEC block END-EXEC

EXEC blocks accumulate all lines between EXEC SQL/CICS and END-EXEC, then delegate to parseExecSqlBlock() or parseExecCicsBlock() for detailed extraction.

Excluded Paragraph Names

The following names are excluded from paragraph detection to avoid false positives from division/section headers:

DECLARATIVES, END, PROCEDURE, IDENTIFICATION,
ENVIRONMENT, DATA, WORKING-STORAGE, LINKAGE,
FILE, LOCAL-STORAGE, COMMUNICATION, REPORT,
SCREEN, INPUT-OUTPUT, CONFIGURATION

Additionally, paragraph candidates containing DIVISION or SECTION as substrings are excluded.

MOVE Skip List (Figurative Constants)

MOVE statements where the source is a figurative constant are skipped:

SPACES, ZEROS, ZEROES, LOW-VALUES, LOW-VALUE,
HIGH-VALUES, HIGH-VALUE, QUOTES, QUOTE, ALL

Source Files

  • gitnexus/src/core/ingestion/cobol-preprocessor.ts -- preprocessCobolSource(), extractCobolSymbolsWithRegex(), all regex constants