gitnexus 1.6.6-rc.70 → 1.6.6-rc.71
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/ingestion/languages/cobol/captures.d.ts +13 -0
- package/dist/core/ingestion/languages/cobol/captures.js +227 -0
- package/dist/core/ingestion/languages/cobol/index.d.ts +14 -0
- package/dist/core/ingestion/languages/cobol/index.js +14 -0
- package/dist/core/ingestion/languages/cobol/interpret.d.ts +46 -0
- package/dist/core/ingestion/languages/cobol/interpret.js +79 -0
- package/dist/core/ingestion/languages/cobol/scope-resolver.d.ts +13 -0
- package/dist/core/ingestion/languages/cobol/scope-resolver.js +69 -0
- package/dist/core/ingestion/languages/cobol.js +9 -2
- package/dist/core/ingestion/registry-primary-flag.d.ts +3 -2
- package/dist/core/ingestion/registry-primary-flag.js +3 -2
- package/dist/core/ingestion/scope-resolution/pipeline/registry.js +2 -0
- package/package.json +1 -1
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `emitScopeCaptures` for COBOL.
|
|
3
|
+
*
|
|
4
|
+
* Wraps the existing regex tagger (`extractCobolSymbolsWithRegex`) and
|
|
5
|
+
* produces parser-agnostic `CaptureMatch[]` matching the RFC §5.1
|
|
6
|
+
* vocabulary. The central `ScopeExtractor` consumes these captures
|
|
7
|
+
* without knowing whether they came from tree-sitter or regex.
|
|
8
|
+
*
|
|
9
|
+
* Pure given the input source text. No I/O, no globals consulted.
|
|
10
|
+
* The regex tagger is synchronous — no async needed.
|
|
11
|
+
*/
|
|
12
|
+
import type { CaptureMatch } from '../../../../_shared/index.js';
|
|
13
|
+
export declare function emitCobolScopeCaptures(sourceText: string, _filePath: string, _cachedTree?: unknown): readonly CaptureMatch[];
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `emitScopeCaptures` for COBOL.
|
|
3
|
+
*
|
|
4
|
+
* Wraps the existing regex tagger (`extractCobolSymbolsWithRegex`) and
|
|
5
|
+
* produces parser-agnostic `CaptureMatch[]` matching the RFC §5.1
|
|
6
|
+
* vocabulary. The central `ScopeExtractor` consumes these captures
|
|
7
|
+
* without knowing whether they came from tree-sitter or regex.
|
|
8
|
+
*
|
|
9
|
+
* Pure given the input source text. No I/O, no globals consulted.
|
|
10
|
+
* The regex tagger is synchronous — no async needed.
|
|
11
|
+
*/
|
|
12
|
+
import { extractCobolSymbolsWithRegex, preprocessCobolSource, } from '../../cobol/cobol-preprocessor.js';
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
// Capture building helpers
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
function capture(name, range, text) {
|
|
17
|
+
return { name, range, text };
|
|
18
|
+
}
|
|
19
|
+
function rangeOf(startLine, startCol, endLine, endCol) {
|
|
20
|
+
return { startLine, startCol, endLine, endCol };
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Build a single CaptureMatch from a record of captures.
|
|
24
|
+
* Returns null if the record is empty.
|
|
25
|
+
*/
|
|
26
|
+
function matchFrom(grouped) {
|
|
27
|
+
if (Object.keys(grouped).length === 0)
|
|
28
|
+
return null;
|
|
29
|
+
return Object.freeze(grouped);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Compute end column for a single-line capture from the source lines array.
|
|
33
|
+
*/
|
|
34
|
+
function endColFrom(line) {
|
|
35
|
+
return line.length > 0 ? line.length - 1 : 0;
|
|
36
|
+
}
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
// Main entry point
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
export function emitCobolScopeCaptures(sourceText, _filePath, _cachedTree) {
|
|
41
|
+
const lines = sourceText.split(/\r?\n/);
|
|
42
|
+
// Preprocess: strip patch markers from columns 1-6
|
|
43
|
+
const cleaned = preprocessCobolSource(sourceText);
|
|
44
|
+
// Run the regex tagger on the preprocessed source
|
|
45
|
+
const extracted = extractCobolSymbolsWithRegex(cleaned, _filePath);
|
|
46
|
+
const out = [];
|
|
47
|
+
// ── 1. PROGRAM-ID → @scope.module ───────────────────────────────────
|
|
48
|
+
// The primary program name (first PROGRAM-ID encountered)
|
|
49
|
+
if (extracted.programName) {
|
|
50
|
+
const name = extracted.programName;
|
|
51
|
+
const lastLine = lines.length;
|
|
52
|
+
const progDef = extracted.programs.find((p) => p.name.toUpperCase() === name.toUpperCase());
|
|
53
|
+
const startLine = progDef?.startLine ?? 1;
|
|
54
|
+
const endLine = progDef?.endLine ?? lastLine;
|
|
55
|
+
const startCol = 0;
|
|
56
|
+
const endCol = endColFrom(lines[Math.min(endLine, lines.length) - 1] ?? '');
|
|
57
|
+
const progIdLine = findProgramIdLine(cleaned, name);
|
|
58
|
+
const nameRange = progIdLine !== -1
|
|
59
|
+
? rangeOf(progIdLine, 7, progIdLine, lines[progIdLine - 1]?.length ?? endCol)
|
|
60
|
+
: rangeOf(startLine, startCol, endLine, endCol);
|
|
61
|
+
const grouped = {
|
|
62
|
+
'@scope.module': capture('@scope.module', nameRange, name),
|
|
63
|
+
'@declaration.program': capture('@declaration.program', rangeOf(startLine, startCol, endLine, endCol), name),
|
|
64
|
+
'@declaration.name': capture('@declaration.name', nameRange, name),
|
|
65
|
+
};
|
|
66
|
+
if (progDef?.procedureUsing && progDef.procedureUsing.length > 0) {
|
|
67
|
+
grouped['@declaration.parameter-count'] = capture('@declaration.parameter-count', nameRange, String(progDef.procedureUsing.length));
|
|
68
|
+
}
|
|
69
|
+
const m = matchFrom(grouped);
|
|
70
|
+
if (m !== null)
|
|
71
|
+
out.push(m);
|
|
72
|
+
}
|
|
73
|
+
// ── 2. Nested / additional programs → @scope.module ──────────────
|
|
74
|
+
for (const prog of extracted.programs) {
|
|
75
|
+
if (extracted.programName && prog.name.toUpperCase() === extracted.programName.toUpperCase())
|
|
76
|
+
continue;
|
|
77
|
+
const startLine = prog.startLine;
|
|
78
|
+
const endLine = prog.endLine;
|
|
79
|
+
const startCol = 0;
|
|
80
|
+
const endCol = endColFrom(lines[Math.min(endLine, lines.length) - 1] ?? '');
|
|
81
|
+
const progIdLine = findProgramIdLine(cleaned, prog.name);
|
|
82
|
+
const nameRange = progIdLine !== -1
|
|
83
|
+
? rangeOf(progIdLine, 7, progIdLine, lines[progIdLine - 1]?.length ?? endCol)
|
|
84
|
+
: rangeOf(startLine, startCol, endLine, endCol);
|
|
85
|
+
const grouped = {
|
|
86
|
+
'@scope.module': capture('@scope.module', nameRange, prog.name),
|
|
87
|
+
'@declaration.program': capture('@declaration.program', rangeOf(startLine, startCol, endLine, endCol), prog.name),
|
|
88
|
+
'@declaration.name': capture('@declaration.name', nameRange, prog.name),
|
|
89
|
+
};
|
|
90
|
+
if (prog.procedureUsing && prog.procedureUsing.length > 0) {
|
|
91
|
+
grouped['@declaration.parameter-count'] = capture('@declaration.parameter-count', nameRange, String(prog.procedureUsing.length));
|
|
92
|
+
}
|
|
93
|
+
const m = matchFrom(grouped);
|
|
94
|
+
if (m !== null)
|
|
95
|
+
out.push(m);
|
|
96
|
+
}
|
|
97
|
+
// ── 3. PROCEDURE DIVISION sections → @scope.function ─────────────
|
|
98
|
+
for (const section of extracted.sections) {
|
|
99
|
+
const lineIdx = section.line - 1;
|
|
100
|
+
if (lineIdx < 0 || lineIdx >= lines.length)
|
|
101
|
+
continue;
|
|
102
|
+
const sectionLine = lines[lineIdx];
|
|
103
|
+
const startCol = 0;
|
|
104
|
+
const endCol = endColFrom(sectionLine);
|
|
105
|
+
const nameRange = rangeOf(section.line, startCol, section.line, endCol);
|
|
106
|
+
const grouped = {
|
|
107
|
+
'@scope.function': capture('@scope.function', nameRange, section.name),
|
|
108
|
+
'@declaration.function': capture('@declaration.function', nameRange, section.name),
|
|
109
|
+
'@declaration.name': capture('@declaration.name', nameRange, section.name),
|
|
110
|
+
};
|
|
111
|
+
const m = matchFrom(grouped);
|
|
112
|
+
if (m !== null)
|
|
113
|
+
out.push(m);
|
|
114
|
+
}
|
|
115
|
+
// ── 4. Paragraphs → @scope.function ──────────────────────────────
|
|
116
|
+
for (const para of extracted.paragraphs) {
|
|
117
|
+
const lineIdx = para.line - 1;
|
|
118
|
+
if (lineIdx < 0 || lineIdx >= lines.length)
|
|
119
|
+
continue;
|
|
120
|
+
const paraLine = lines[lineIdx];
|
|
121
|
+
const startCol = 0;
|
|
122
|
+
const endCol = endColFrom(paraLine);
|
|
123
|
+
const nameRange = rangeOf(para.line, startCol, para.line, endCol);
|
|
124
|
+
const grouped = {
|
|
125
|
+
'@scope.function': capture('@scope.function', nameRange, para.name),
|
|
126
|
+
'@declaration.function': capture('@declaration.function', nameRange, para.name),
|
|
127
|
+
'@declaration.name': capture('@declaration.name', nameRange, para.name),
|
|
128
|
+
};
|
|
129
|
+
const m = matchFrom(grouped);
|
|
130
|
+
if (m !== null)
|
|
131
|
+
out.push(m);
|
|
132
|
+
}
|
|
133
|
+
// ── 5. COPY → @import.statement ──────────────────────────────────
|
|
134
|
+
for (const copy of extracted.copies) {
|
|
135
|
+
const lineIdx = copy.line - 1;
|
|
136
|
+
if (lineIdx < 0 || lineIdx >= lines.length)
|
|
137
|
+
continue;
|
|
138
|
+
const copyLine = lines[lineIdx];
|
|
139
|
+
const startCol = 0;
|
|
140
|
+
const endCol = endColFrom(copyLine);
|
|
141
|
+
const stmtRange = rangeOf(copy.line, startCol, copy.line, endCol);
|
|
142
|
+
const grouped = {
|
|
143
|
+
'@import.statement': capture('@import.statement', stmtRange, copy.target),
|
|
144
|
+
'@import.name': capture('@import.name', stmtRange, copy.target),
|
|
145
|
+
};
|
|
146
|
+
const m = matchFrom(grouped);
|
|
147
|
+
if (m !== null)
|
|
148
|
+
out.push(m);
|
|
149
|
+
}
|
|
150
|
+
// ── 6. CALL (quoted/referenced) → @reference.call ────────────────
|
|
151
|
+
for (const call of extracted.calls) {
|
|
152
|
+
const lineIdx = call.line - 1;
|
|
153
|
+
if (lineIdx < 0 || lineIdx >= lines.length)
|
|
154
|
+
continue;
|
|
155
|
+
const callLine = lines[lineIdx];
|
|
156
|
+
const startCol = 0;
|
|
157
|
+
const endCol = endColFrom(callLine);
|
|
158
|
+
const stmtRange = rangeOf(call.line, startCol, call.line, endCol);
|
|
159
|
+
const grouped = {
|
|
160
|
+
'@reference.call': capture('@reference.call', stmtRange, call.target),
|
|
161
|
+
'@reference.name': capture('@reference.name', stmtRange, call.target),
|
|
162
|
+
};
|
|
163
|
+
// Arity from CALL USING parameters
|
|
164
|
+
if (call.parameters && call.parameters.length > 0) {
|
|
165
|
+
grouped['@reference.arity'] = capture('@reference.arity', stmtRange, String(call.parameters.length));
|
|
166
|
+
}
|
|
167
|
+
const m = matchFrom(grouped);
|
|
168
|
+
if (m !== null)
|
|
169
|
+
out.push(m);
|
|
170
|
+
}
|
|
171
|
+
// ── 7. PERFORM → @reference.call ─────────────────────────────────
|
|
172
|
+
for (const perf of extracted.performs) {
|
|
173
|
+
const lineIdx = perf.line - 1;
|
|
174
|
+
if (lineIdx < 0 || lineIdx >= lines.length)
|
|
175
|
+
continue;
|
|
176
|
+
const perfLine = lines[lineIdx];
|
|
177
|
+
const startCol = 0;
|
|
178
|
+
const endCol = endColFrom(perfLine);
|
|
179
|
+
const stmtRange = rangeOf(perf.line, startCol, perf.line, endCol);
|
|
180
|
+
const grouped = {
|
|
181
|
+
'@reference.call': capture('@reference.call', stmtRange, perf.target),
|
|
182
|
+
'@reference.name': capture('@reference.name', stmtRange, perf.target),
|
|
183
|
+
};
|
|
184
|
+
const m = matchFrom(grouped);
|
|
185
|
+
if (m !== null)
|
|
186
|
+
out.push(m);
|
|
187
|
+
}
|
|
188
|
+
// ── 8. GO TO → @reference.call ───────────────────────────────────
|
|
189
|
+
for (const gt of extracted.gotos) {
|
|
190
|
+
const lineIdx = gt.line - 1;
|
|
191
|
+
if (lineIdx < 0 || lineIdx >= lines.length)
|
|
192
|
+
continue;
|
|
193
|
+
const gtLine = lines[lineIdx];
|
|
194
|
+
const startCol = 0;
|
|
195
|
+
const endCol = endColFrom(gtLine);
|
|
196
|
+
const stmtRange = rangeOf(gt.line, startCol, gt.line, endCol);
|
|
197
|
+
const grouped = {
|
|
198
|
+
'@reference.call': capture('@reference.call', stmtRange, gt.target),
|
|
199
|
+
'@reference.name': capture('@reference.name', stmtRange, gt.target),
|
|
200
|
+
};
|
|
201
|
+
const m = matchFrom(grouped);
|
|
202
|
+
if (m !== null)
|
|
203
|
+
out.push(m);
|
|
204
|
+
}
|
|
205
|
+
return out;
|
|
206
|
+
}
|
|
207
|
+
// ---------------------------------------------------------------------------
|
|
208
|
+
// Helpers
|
|
209
|
+
// ---------------------------------------------------------------------------
|
|
210
|
+
/**
|
|
211
|
+
* Find the PROGRAM-ID. line for a given program name in the cleaned source.
|
|
212
|
+
* Returns 1-based line number, or -1 if not found.
|
|
213
|
+
*/
|
|
214
|
+
function findProgramIdLine(cleanedSource, programName) {
|
|
215
|
+
const lines = cleanedSource.split(/\r?\n/);
|
|
216
|
+
const upper = programName.toUpperCase();
|
|
217
|
+
const re = new RegExp(`\\bPROGRAM-ID\\.\\s*${escapeRegex(upper)}\\b`, 'i');
|
|
218
|
+
for (let i = 0; i < lines.length; i++) {
|
|
219
|
+
if (re.test(lines[i]))
|
|
220
|
+
return i + 1; // 1-based
|
|
221
|
+
}
|
|
222
|
+
return -1;
|
|
223
|
+
}
|
|
224
|
+
/** Simple regex escape for special chars. */
|
|
225
|
+
function escapeRegex(s) {
|
|
226
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
227
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* COBOL scope-resolution public API barrel.
|
|
3
|
+
*
|
|
4
|
+
* Consumers should import from this file rather than the individual
|
|
5
|
+
* modules — that keeps the per-hook organization an implementation
|
|
6
|
+
* detail we can refactor without touching the provider wiring.
|
|
7
|
+
*
|
|
8
|
+
* Module layout:
|
|
9
|
+
*
|
|
10
|
+
* - `captures.ts` — `emitCobolScopeCaptures` (wraps the regex tagger)
|
|
11
|
+
* - `interpret.ts` — import/type-binding/receiver hooks
|
|
12
|
+
*/
|
|
13
|
+
export { emitCobolScopeCaptures } from './captures.js';
|
|
14
|
+
export { interpretCobolImport, interpretCobolTypeBinding, cobolImportOwningScope, cobolReceiverBinding, } from './interpret.js';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* COBOL scope-resolution public API barrel.
|
|
3
|
+
*
|
|
4
|
+
* Consumers should import from this file rather than the individual
|
|
5
|
+
* modules — that keeps the per-hook organization an implementation
|
|
6
|
+
* detail we can refactor without touching the provider wiring.
|
|
7
|
+
*
|
|
8
|
+
* Module layout:
|
|
9
|
+
*
|
|
10
|
+
* - `captures.ts` — `emitCobolScopeCaptures` (wraps the regex tagger)
|
|
11
|
+
* - `interpret.ts` — import/type-binding/receiver hooks
|
|
12
|
+
*/
|
|
13
|
+
export { emitCobolScopeCaptures } from './captures.js';
|
|
14
|
+
export { interpretCobolImport, interpretCobolTypeBinding, cobolImportOwningScope, cobolReceiverBinding, } from './interpret.js';
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* COBOL scope-resolution interpret hooks.
|
|
3
|
+
*
|
|
4
|
+
* Interprets raw `@import.statement` capture matches (from COPY statements)
|
|
5
|
+
* into `ParsedImport` for the central finalize algorithm.
|
|
6
|
+
*
|
|
7
|
+
* COBOL's import semantic is simple: `COPY bookname` means the copybook's
|
|
8
|
+
* content is inlined at compile time. There is no module-system equivalent
|
|
9
|
+
* of `export` — everything is text-inclusion. The scope-resolution pipeline
|
|
10
|
+
* models this as a `'named'` import where the imported name is the copybook
|
|
11
|
+
* name and the target is the copybook file path.
|
|
12
|
+
*/
|
|
13
|
+
import type { CaptureMatch, ParsedImport, ParsedTypeBinding, ScopeId, ScopeTree, Scope, TypeRef } from '../../../../_shared/index.js';
|
|
14
|
+
/**
|
|
15
|
+
* Interpret a COPY statement as a `ParsedImport`.
|
|
16
|
+
*
|
|
17
|
+
* The `@import.name` capture contains the copybook target name (e.g.,
|
|
18
|
+
* `CPSESP` from `COPY CPSESP.`). Returns a `'named'` import with the
|
|
19
|
+
* copybook name as both `localName` and `importedName`.
|
|
20
|
+
*
|
|
21
|
+
* Returns `null` for any match that doesn't carry an `@import.name` (e.g.,
|
|
22
|
+
* malformed COPY statements the regex tagger might emit).
|
|
23
|
+
*/
|
|
24
|
+
export declare function interpretCobolImport(match: CaptureMatch): ParsedImport | null;
|
|
25
|
+
/**
|
|
26
|
+
* COBOL has no type system — no type bindings to interpret.
|
|
27
|
+
* Always returns `null`.
|
|
28
|
+
*/
|
|
29
|
+
export declare function interpretCobolTypeBinding(_match: CaptureMatch): ParsedTypeBinding | null;
|
|
30
|
+
/**
|
|
31
|
+
* COPY statements in COBOL are module-level — they expand inline at
|
|
32
|
+
* compile time and their bindings belong to the enclosing PROGRAM-ID
|
|
33
|
+
* (Module) scope. Walk up from the innermost scope through ancestors
|
|
34
|
+
* to find the enclosing Module scope.
|
|
35
|
+
*
|
|
36
|
+
* For the edge case of a COPY inside a paragraph (unusual but possible
|
|
37
|
+
* with some vendors), we walk the scope tree to ensure the import is
|
|
38
|
+
* attached to the program scope, not the paragraph Function scope.
|
|
39
|
+
*/
|
|
40
|
+
export declare function cobolImportOwningScope(_imp: ParsedImport, innermost: Scope, tree: ScopeTree): ScopeId | null;
|
|
41
|
+
/**
|
|
42
|
+
* COBOL has no implicit receiver (no `self`, `this`, or equivalent).
|
|
43
|
+
* All function calls are explicit CALL statements or PERFORM/GO TO
|
|
44
|
+
* control flow. Always returns `null`.
|
|
45
|
+
*/
|
|
46
|
+
export declare function cobolReceiverBinding(_functionScope: Scope): TypeRef | null;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* COBOL scope-resolution interpret hooks.
|
|
3
|
+
*
|
|
4
|
+
* Interprets raw `@import.statement` capture matches (from COPY statements)
|
|
5
|
+
* into `ParsedImport` for the central finalize algorithm.
|
|
6
|
+
*
|
|
7
|
+
* COBOL's import semantic is simple: `COPY bookname` means the copybook's
|
|
8
|
+
* content is inlined at compile time. There is no module-system equivalent
|
|
9
|
+
* of `export` — everything is text-inclusion. The scope-resolution pipeline
|
|
10
|
+
* models this as a `'named'` import where the imported name is the copybook
|
|
11
|
+
* name and the target is the copybook file path.
|
|
12
|
+
*/
|
|
13
|
+
// ─── interpretImport ──────────────────────────────────────────────────────
|
|
14
|
+
/**
|
|
15
|
+
* Interpret a COPY statement as a `ParsedImport`.
|
|
16
|
+
*
|
|
17
|
+
* The `@import.name` capture contains the copybook target name (e.g.,
|
|
18
|
+
* `CPSESP` from `COPY CPSESP.`). Returns a `'named'` import with the
|
|
19
|
+
* copybook name as both `localName` and `importedName`.
|
|
20
|
+
*
|
|
21
|
+
* Returns `null` for any match that doesn't carry an `@import.name` (e.g.,
|
|
22
|
+
* malformed COPY statements the regex tagger might emit).
|
|
23
|
+
*/
|
|
24
|
+
export function interpretCobolImport(match) {
|
|
25
|
+
const nameCap = match['@import.name'];
|
|
26
|
+
if (nameCap === undefined)
|
|
27
|
+
return null;
|
|
28
|
+
const name = nameCap.text;
|
|
29
|
+
if (name === '')
|
|
30
|
+
return null;
|
|
31
|
+
return {
|
|
32
|
+
kind: 'named',
|
|
33
|
+
localName: name,
|
|
34
|
+
importedName: name,
|
|
35
|
+
targetRaw: name,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
// ─── interpretTypeBinding ─────────────────────────────────────────────────
|
|
39
|
+
/**
|
|
40
|
+
* COBOL has no type system — no type bindings to interpret.
|
|
41
|
+
* Always returns `null`.
|
|
42
|
+
*/
|
|
43
|
+
export function interpretCobolTypeBinding(_match) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
// ─── importOwningScope ────────────────────────────────────────────────────
|
|
47
|
+
/**
|
|
48
|
+
* COPY statements in COBOL are module-level — they expand inline at
|
|
49
|
+
* compile time and their bindings belong to the enclosing PROGRAM-ID
|
|
50
|
+
* (Module) scope. Walk up from the innermost scope through ancestors
|
|
51
|
+
* to find the enclosing Module scope.
|
|
52
|
+
*
|
|
53
|
+
* For the edge case of a COPY inside a paragraph (unusual but possible
|
|
54
|
+
* with some vendors), we walk the scope tree to ensure the import is
|
|
55
|
+
* attached to the program scope, not the paragraph Function scope.
|
|
56
|
+
*/
|
|
57
|
+
export function cobolImportOwningScope(_imp, innermost, tree) {
|
|
58
|
+
// If already in a Module scope, use it directly.
|
|
59
|
+
if (innermost.kind === 'Module')
|
|
60
|
+
return innermost.id;
|
|
61
|
+
// Walk through ancestors to find the enclosing Module.
|
|
62
|
+
const ancestors = tree.getAncestors(innermost.id);
|
|
63
|
+
for (const ancId of ancestors) {
|
|
64
|
+
const anc = tree.getScope(ancId);
|
|
65
|
+
if (anc !== undefined && anc.kind === 'Module')
|
|
66
|
+
return ancId;
|
|
67
|
+
}
|
|
68
|
+
// Fallback: delegate to central default.
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
// ─── receiverBinding ──────────────────────────────────────────────────────
|
|
72
|
+
/**
|
|
73
|
+
* COBOL has no implicit receiver (no `self`, `this`, or equivalent).
|
|
74
|
+
* All function calls are explicit CALL statements or PERFORM/GO TO
|
|
75
|
+
* control flow. Always returns `null`.
|
|
76
|
+
*/
|
|
77
|
+
export function cobolReceiverBinding(_functionScope) {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* COBOL `ScopeResolver` registered in `SCOPE_RESOLVERS` and consumed
|
|
3
|
+
* by the generic `runScopeResolution` orchestrator.
|
|
4
|
+
*
|
|
5
|
+
* The provider is a thin wiring object — COBOL's simple scope model
|
|
6
|
+
* (Module + Function only, no inheritance, no type system) plugs into
|
|
7
|
+
* `runScopeResolution` with minimal configuration.
|
|
8
|
+
*
|
|
9
|
+
* Reference: `languages/python/scope-resolver.ts`.
|
|
10
|
+
*/
|
|
11
|
+
import type { ScopeResolver } from '../../scope-resolution/contract/scope-resolver.js';
|
|
12
|
+
declare const cobolScopeResolver: ScopeResolver;
|
|
13
|
+
export { cobolScopeResolver };
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* COBOL `ScopeResolver` registered in `SCOPE_RESOLVERS` and consumed
|
|
3
|
+
* by the generic `runScopeResolution` orchestrator.
|
|
4
|
+
*
|
|
5
|
+
* The provider is a thin wiring object — COBOL's simple scope model
|
|
6
|
+
* (Module + Function only, no inheritance, no type system) plugs into
|
|
7
|
+
* `runScopeResolution` with minimal configuration.
|
|
8
|
+
*
|
|
9
|
+
* Reference: `languages/python/scope-resolver.ts`.
|
|
10
|
+
*/
|
|
11
|
+
import path from 'node:path';
|
|
12
|
+
import { SupportedLanguages } from '../../../../_shared/index.js';
|
|
13
|
+
import { populateClassOwnedMembers } from '../../scope-resolution/scope/walkers.js';
|
|
14
|
+
import { cobolProvider } from '../cobol.js';
|
|
15
|
+
// Copybook file extensions for COPY name resolution
|
|
16
|
+
const COPYBOOK_EXTENSIONS = new Set(['.cpy', '.copybook']);
|
|
17
|
+
const cobolScopeResolver = {
|
|
18
|
+
language: SupportedLanguages.Cobol,
|
|
19
|
+
languageProvider: cobolProvider,
|
|
20
|
+
importEdgeReason: 'cobol-scope: copy',
|
|
21
|
+
// ── Resolve COPY bookname to file path ─────────────────────────────
|
|
22
|
+
resolveImportTarget: (targetRaw, _fromFile, allFilePaths) => {
|
|
23
|
+
const upper = targetRaw.toUpperCase();
|
|
24
|
+
// Check copybook files first
|
|
25
|
+
for (const fp of allFilePaths) {
|
|
26
|
+
const ext = path.extname(fp).toLowerCase();
|
|
27
|
+
if (!COPYBOOK_EXTENSIONS.has(ext))
|
|
28
|
+
continue;
|
|
29
|
+
const basename = path.basename(fp, ext).toUpperCase();
|
|
30
|
+
if (basename === upper)
|
|
31
|
+
return fp;
|
|
32
|
+
}
|
|
33
|
+
// Also search COBOL source files (.cbl, .cob, .cobol)
|
|
34
|
+
const COBOL_SOURCE_EXTS = new Set(['.cbl', '.cob', '.cobol']);
|
|
35
|
+
for (const fp of allFilePaths) {
|
|
36
|
+
const ext = path.extname(fp).toLowerCase();
|
|
37
|
+
if (!COBOL_SOURCE_EXTS.has(ext))
|
|
38
|
+
continue;
|
|
39
|
+
const basename = path.basename(fp, ext).toUpperCase();
|
|
40
|
+
if (basename === upper)
|
|
41
|
+
return fp;
|
|
42
|
+
}
|
|
43
|
+
return null;
|
|
44
|
+
},
|
|
45
|
+
// COBOL has no binding-merge rules beyond the default (local-first-then-imports).
|
|
46
|
+
mergeBindings: (existing) => [...existing],
|
|
47
|
+
// COBOL arity: compare CALL USING param count against def's parameterCount.
|
|
48
|
+
// COBOL requires exact arity match for CALL USING.
|
|
49
|
+
arityCompatibility: (callsite, def) => {
|
|
50
|
+
if (callsite.arity === undefined)
|
|
51
|
+
return 'unknown';
|
|
52
|
+
const defParamCount = def.parameterCount;
|
|
53
|
+
if (defParamCount === undefined)
|
|
54
|
+
return 'unknown';
|
|
55
|
+
if (callsite.arity === defParamCount)
|
|
56
|
+
return 'compatible';
|
|
57
|
+
return 'incompatible';
|
|
58
|
+
},
|
|
59
|
+
// No inheritance in COBOL — empty MRO map.
|
|
60
|
+
buildMro: () => new Map(),
|
|
61
|
+
// Everything lives under the PROGRAM-ID Module scope.
|
|
62
|
+
populateOwners: (parsed) => populateClassOwnedMembers(parsed),
|
|
63
|
+
// COBOL has no super calls.
|
|
64
|
+
isSuperReceiver: () => false,
|
|
65
|
+
// ── Optional toggles ─────────────────────────────────────────────
|
|
66
|
+
fieldFallbackOnMethodLookup: false,
|
|
67
|
+
propagatesReturnTypesAcrossImports: false,
|
|
68
|
+
};
|
|
69
|
+
export { cobolScopeResolver };
|
|
@@ -6,11 +6,13 @@
|
|
|
6
6
|
* processed by cobol-processor.ts in pipeline Phase 2.6, not by the
|
|
7
7
|
* tree-sitter pipeline.
|
|
8
8
|
*
|
|
9
|
-
* This provider
|
|
10
|
-
*
|
|
9
|
+
* This provider supports scope-based resolution (RFC #909 Ring 3) via
|
|
10
|
+
* `emitScopeCaptures` which wraps the regex tagger. COPY statements are
|
|
11
|
+
* interpreted as imports; there is no type system and no implicit receiver.
|
|
11
12
|
*/
|
|
12
13
|
import { SupportedLanguages } from '../../../_shared/index.js';
|
|
13
14
|
import { defineLanguage } from '../language-provider.js';
|
|
15
|
+
import { emitCobolScopeCaptures, interpretCobolImport, cobolImportOwningScope, cobolReceiverBinding, } from './cobol/index.js';
|
|
14
16
|
export const cobolProvider = defineLanguage({
|
|
15
17
|
id: SupportedLanguages.Cobol,
|
|
16
18
|
parseStrategy: 'standalone',
|
|
@@ -25,4 +27,9 @@ export const cobolProvider = defineLanguage({
|
|
|
25
27
|
},
|
|
26
28
|
exportChecker: () => false,
|
|
27
29
|
importResolver: () => null,
|
|
30
|
+
// ── Scope-resolution hooks ───────────────────────────────────────
|
|
31
|
+
emitScopeCaptures: emitCobolScopeCaptures,
|
|
32
|
+
interpretImport: interpretCobolImport,
|
|
33
|
+
importOwningScope: cobolImportOwningScope,
|
|
34
|
+
receiverBinding: cobolReceiverBinding,
|
|
28
35
|
});
|
|
@@ -11,8 +11,9 @@
|
|
|
11
11
|
* ## Contract
|
|
12
12
|
*
|
|
13
13
|
* - Env-var name per language: `REGISTRY_PRIMARY_<UPPER(enum-value)>`.
|
|
14
|
-
*
|
|
15
|
-
*
|
|
14
|
+
* Example: `SupportedLanguages.Python` → `REGISTRY_PRIMARY_PYTHON`;
|
|
15
|
+
* `SupportedLanguages.CPlusPlus` (value `'cpp'`) → `REGISTRY_PRIMARY_CPP`.
|
|
16
|
+
* `SupportedLanguages.Cobol` (value `'cobol'`) → `REGISTRY_PRIMARY_COBOL`.
|
|
16
17
|
* - Truthy values: `'true'`, `'1'`, `'yes'` (case-insensitive,
|
|
17
18
|
* whitespace-trimmed). Anything else — including `undefined`, empty
|
|
18
19
|
* string, or unknown tokens — is `false`.
|
|
@@ -11,8 +11,9 @@
|
|
|
11
11
|
* ## Contract
|
|
12
12
|
*
|
|
13
13
|
* - Env-var name per language: `REGISTRY_PRIMARY_<UPPER(enum-value)>`.
|
|
14
|
-
*
|
|
15
|
-
*
|
|
14
|
+
* Example: `SupportedLanguages.Python` → `REGISTRY_PRIMARY_PYTHON`;
|
|
15
|
+
* `SupportedLanguages.CPlusPlus` (value `'cpp'`) → `REGISTRY_PRIMARY_CPP`.
|
|
16
|
+
* `SupportedLanguages.Cobol` (value `'cobol'`) → `REGISTRY_PRIMARY_COBOL`.
|
|
16
17
|
* - Truthy values: `'true'`, `'1'`, `'yes'` (case-insensitive,
|
|
17
18
|
* whitespace-trimmed). Anything else — including `undefined`, empty
|
|
18
19
|
* string, or unknown tokens — is `false`.
|
|
@@ -21,6 +21,7 @@ import { rustScopeResolver } from '../../languages/rust/scope-resolver.js';
|
|
|
21
21
|
import { javascriptScopeResolver } from '../../languages/javascript/scope-resolver.js';
|
|
22
22
|
import { kotlinScopeResolver } from '../../languages/kotlin/scope-resolver.js';
|
|
23
23
|
import { rubyScopeResolver } from '../../languages/ruby/scope-resolver.js';
|
|
24
|
+
import { cobolScopeResolver } from '../../languages/cobol/scope-resolver.js';
|
|
24
25
|
/** Map of `SupportedLanguages` → `ScopeResolver`. The phase iterates
|
|
25
26
|
* this map intersected with `MIGRATED_LANGUAGES` (the per-language
|
|
26
27
|
* flag set) so adding a resolver here without flipping the flag is
|
|
@@ -38,4 +39,5 @@ export const SCOPE_RESOLVERS = new Map([
|
|
|
38
39
|
[SupportedLanguages.JavaScript, javascriptScopeResolver],
|
|
39
40
|
[SupportedLanguages.Kotlin, kotlinScopeResolver],
|
|
40
41
|
[SupportedLanguages.Ruby, rubyScopeResolver],
|
|
42
|
+
[SupportedLanguages.Cobol, cobolScopeResolver],
|
|
41
43
|
]);
|
package/package.json
CHANGED