@miguelduran14/knowflow 0.1.1
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/LICENSE +201 -0
- package/README.md +182 -0
- package/dist/advisories.d.ts +16 -0
- package/dist/advisories.js +364 -0
- package/dist/advisories.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +104 -0
- package/dist/cli.js.map +1 -0
- package/dist/copy-resolver.d.ts +22 -0
- package/dist/copy-resolver.js +74 -0
- package/dist/copy-resolver.js.map +1 -0
- package/dist/dataflow.d.ts +2 -0
- package/dist/dataflow.js +88 -0
- package/dist/dataflow.js.map +1 -0
- package/dist/dossier.d.ts +20 -0
- package/dist/dossier.js +331 -0
- package/dist/dossier.js.map +1 -0
- package/dist/explain/claude.d.ts +14 -0
- package/dist/explain/claude.js +91 -0
- package/dist/explain/claude.js.map +1 -0
- package/dist/explain/explain.d.ts +72 -0
- package/dist/explain/explain.js +126 -0
- package/dist/explain/explain.js.map +1 -0
- package/dist/explain/facts.d.ts +23 -0
- package/dist/explain/facts.js +266 -0
- package/dist/explain/facts.js.map +1 -0
- package/dist/explain/fake.d.ts +14 -0
- package/dist/explain/fake.js +27 -0
- package/dist/explain/fake.js.map +1 -0
- package/dist/explain/openai.d.ts +42 -0
- package/dist/explain/openai.js +102 -0
- package/dist/explain/openai.js.map +1 -0
- package/dist/explain/provider.d.ts +19 -0
- package/dist/explain/provider.js +2 -0
- package/dist/explain/provider.js.map +1 -0
- package/dist/explain/sse.d.ts +10 -0
- package/dist/explain/sse.js +44 -0
- package/dist/explain/sse.js.map +1 -0
- package/dist/flow-parser.d.ts +10 -0
- package/dist/flow-parser.js +503 -0
- package/dist/flow-parser.js.map +1 -0
- package/dist/glossary.d.ts +54 -0
- package/dist/glossary.js +340 -0
- package/dist/glossary.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/inventory.d.ts +7 -0
- package/dist/inventory.js +453 -0
- package/dist/inventory.js.map +1 -0
- package/dist/lines.d.ts +15 -0
- package/dist/lines.js +53 -0
- package/dist/lines.js.map +1 -0
- package/dist/linker.d.ts +17 -0
- package/dist/linker.js +56 -0
- package/dist/linker.js.map +1 -0
- package/dist/mermaid.d.ts +27 -0
- package/dist/mermaid.js +133 -0
- package/dist/mermaid.js.map +1 -0
- package/dist/parser.d.ts +19 -0
- package/dist/parser.js +442 -0
- package/dist/parser.js.map +1 -0
- package/dist/pic.d.ts +29 -0
- package/dist/pic.js +197 -0
- package/dist/pic.js.map +1 -0
- package/dist/references.d.ts +24 -0
- package/dist/references.js +906 -0
- package/dist/references.js.map +1 -0
- package/dist/source-lines.d.ts +56 -0
- package/dist/source-lines.js +114 -0
- package/dist/source-lines.js.map +1 -0
- package/dist/types.d.ts +499 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/gui/dist/assets/ChainCanvas-C37KyhnE.js +1 -0
- package/gui/dist/assets/FlowCanvas-BRklWBCp.js +3 -0
- package/gui/dist/assets/RobotProfessor-BtTDTBDq.js +4220 -0
- package/gui/dist/assets/fira-code-cyrillic-400-normal-UC0NFL4U.woff2 +0 -0
- package/gui/dist/assets/fira-code-cyrillic-400-normal-dc1q3VD7.woff +0 -0
- package/gui/dist/assets/fira-code-cyrillic-500-normal-Bu7VCL72.woff +0 -0
- package/gui/dist/assets/fira-code-cyrillic-500-normal-CX35h3Mg.woff2 +0 -0
- package/gui/dist/assets/fira-code-cyrillic-600-normal-BD56RyRT.woff +0 -0
- package/gui/dist/assets/fira-code-cyrillic-600-normal-CPRdbepc.woff2 +0 -0
- package/gui/dist/assets/fira-code-cyrillic-ext-400-normal-Buh61xzB.woff +0 -0
- package/gui/dist/assets/fira-code-cyrillic-ext-400-normal-txZ9Fk_1.woff2 +0 -0
- package/gui/dist/assets/fira-code-cyrillic-ext-500-normal-CYRspHgm.woff2 +0 -0
- package/gui/dist/assets/fira-code-cyrillic-ext-500-normal-CwtLmSFD.woff +0 -0
- package/gui/dist/assets/fira-code-cyrillic-ext-600-normal-B9wE0zmr.woff2 +0 -0
- package/gui/dist/assets/fira-code-cyrillic-ext-600-normal-DldcyWhZ.woff +0 -0
- package/gui/dist/assets/fira-code-greek-400-normal-B2Gh_Y8s.woff2 +0 -0
- package/gui/dist/assets/fira-code-greek-400-normal-DobhaxvF.woff +0 -0
- package/gui/dist/assets/fira-code-greek-500-normal-CVO0IRNt.woff2 +0 -0
- package/gui/dist/assets/fira-code-greek-500-normal-DEXIWiey.woff +0 -0
- package/gui/dist/assets/fira-code-greek-600-normal-D_h3X2X5.woff +0 -0
- package/gui/dist/assets/fira-code-greek-600-normal-Dxp52_By.woff2 +0 -0
- package/gui/dist/assets/fira-code-greek-ext-400-normal-DR7mBgIM.woff2 +0 -0
- package/gui/dist/assets/fira-code-greek-ext-400-normal-DSPUheWg.woff +0 -0
- package/gui/dist/assets/fira-code-greek-ext-500-normal-BO3iq16n.woff2 +0 -0
- package/gui/dist/assets/fira-code-greek-ext-500-normal-CMiNLXER.woff +0 -0
- package/gui/dist/assets/fira-code-greek-ext-600-normal-C-cQDoOk.woff +0 -0
- package/gui/dist/assets/fira-code-greek-ext-600-normal-DtBpFRLc.woff2 +0 -0
- package/gui/dist/assets/fira-code-latin-400-normal-C-QZfXAs.woff +0 -0
- package/gui/dist/assets/fira-code-latin-400-normal-DGosTW8U.woff2 +0 -0
- package/gui/dist/assets/fira-code-latin-500-normal-B6gioGu8.woff2 +0 -0
- package/gui/dist/assets/fira-code-latin-500-normal-aMLC2AMm.woff +0 -0
- package/gui/dist/assets/fira-code-latin-600-normal-BmSSHshu.woff +0 -0
- package/gui/dist/assets/fira-code-latin-600-normal-CvYZpPHg.woff2 +0 -0
- package/gui/dist/assets/fira-code-latin-ext-400-normal-DG73JjOf.woff +0 -0
- package/gui/dist/assets/fira-code-latin-ext-400-normal-KSMg0QLl.woff2 +0 -0
- package/gui/dist/assets/fira-code-latin-ext-500-normal-CaANqXjI.woff +0 -0
- package/gui/dist/assets/fira-code-latin-ext-500-normal-Crd9e_oI.woff2 +0 -0
- package/gui/dist/assets/fira-code-latin-ext-600-normal-0PFQL1eg.woff +0 -0
- package/gui/dist/assets/fira-code-latin-ext-600-normal-Cb7jxoAi.woff2 +0 -0
- package/gui/dist/assets/fira-code-symbols2-400-normal-_QEZCEwe.woff +0 -0
- package/gui/dist/assets/fira-code-symbols2-500-normal-DwsgbIjQ.woff +0 -0
- package/gui/dist/assets/fira-code-symbols2-600-normal-Dvq6IQE5.woff +0 -0
- package/gui/dist/assets/fira-sans-cyrillic-300-normal-BO7tmGom.woff2 +0 -0
- package/gui/dist/assets/fira-sans-cyrillic-300-normal-BtNmWoqn.woff +0 -0
- package/gui/dist/assets/fira-sans-cyrillic-400-normal-CKu2TFQr.woff2 +0 -0
- package/gui/dist/assets/fira-sans-cyrillic-400-normal-D9eUeF-O.woff +0 -0
- package/gui/dist/assets/fira-sans-cyrillic-500-normal-CXfldnQ8.woff2 +0 -0
- package/gui/dist/assets/fira-sans-cyrillic-500-normal-Daski1k2.woff +0 -0
- package/gui/dist/assets/fira-sans-cyrillic-600-normal-CJ5P9t7t.woff +0 -0
- package/gui/dist/assets/fira-sans-cyrillic-600-normal-JSBvCpTs.woff2 +0 -0
- package/gui/dist/assets/fira-sans-cyrillic-700-normal-Bl-HczEO.woff2 +0 -0
- package/gui/dist/assets/fira-sans-cyrillic-700-normal-KyW5QH4H.woff +0 -0
- package/gui/dist/assets/fira-sans-cyrillic-ext-300-normal-BRn8Tsce.woff2 +0 -0
- package/gui/dist/assets/fira-sans-cyrillic-ext-300-normal-DOtRq9zH.woff +0 -0
- package/gui/dist/assets/fira-sans-cyrillic-ext-400-normal-Cycyekte.woff2 +0 -0
- package/gui/dist/assets/fira-sans-cyrillic-ext-400-normal-Dd5dApAy.woff +0 -0
- package/gui/dist/assets/fira-sans-cyrillic-ext-500-normal-Cixivk4R.woff +0 -0
- package/gui/dist/assets/fira-sans-cyrillic-ext-500-normal-Dkk5LlrU.woff2 +0 -0
- package/gui/dist/assets/fira-sans-cyrillic-ext-600-normal-BKgjRriM.woff +0 -0
- package/gui/dist/assets/fira-sans-cyrillic-ext-600-normal-CE6oUZE1.woff2 +0 -0
- package/gui/dist/assets/fira-sans-cyrillic-ext-700-normal-BfO9TR-w.woff2 +0 -0
- package/gui/dist/assets/fira-sans-cyrillic-ext-700-normal-Gcpdxicj.woff +0 -0
- package/gui/dist/assets/fira-sans-greek-300-normal-D3JfF3y9.woff2 +0 -0
- package/gui/dist/assets/fira-sans-greek-300-normal-DSgcXl_1.woff +0 -0
- package/gui/dist/assets/fira-sans-greek-400-normal-BFCFncbp.woff2 +0 -0
- package/gui/dist/assets/fira-sans-greek-400-normal-DQ8sF22y.woff +0 -0
- package/gui/dist/assets/fira-sans-greek-500-normal-C9KT8sAy.woff +0 -0
- package/gui/dist/assets/fira-sans-greek-500-normal-Cy-TUuxE.woff2 +0 -0
- package/gui/dist/assets/fira-sans-greek-600-normal-BlqpLOpF.woff2 +0 -0
- package/gui/dist/assets/fira-sans-greek-600-normal-DfHu91YF.woff +0 -0
- package/gui/dist/assets/fira-sans-greek-700-normal-BVfcrbV7.woff2 +0 -0
- package/gui/dist/assets/fira-sans-greek-700-normal-CJu31rC4.woff +0 -0
- package/gui/dist/assets/fira-sans-greek-ext-300-normal-CodlqOxk.woff +0 -0
- package/gui/dist/assets/fira-sans-greek-ext-300-normal-DVDwbKuK.woff2 +0 -0
- package/gui/dist/assets/fira-sans-greek-ext-400-normal-ByG7UNQ2.woff2 +0 -0
- package/gui/dist/assets/fira-sans-greek-ext-400-normal-ClCfNE6D.woff +0 -0
- package/gui/dist/assets/fira-sans-greek-ext-500-normal-Bem4H9fH.woff +0 -0
- package/gui/dist/assets/fira-sans-greek-ext-500-normal-nq-cjB3m.woff2 +0 -0
- package/gui/dist/assets/fira-sans-greek-ext-600-normal-DPxZoeHL.woff +0 -0
- package/gui/dist/assets/fira-sans-greek-ext-600-normal-fS0evUH3.woff2 +0 -0
- package/gui/dist/assets/fira-sans-greek-ext-700-normal-C1m6bE4Q.woff2 +0 -0
- package/gui/dist/assets/fira-sans-greek-ext-700-normal-C8J5qYMq.woff +0 -0
- package/gui/dist/assets/fira-sans-latin-300-normal-B2LrTgQS.woff2 +0 -0
- package/gui/dist/assets/fira-sans-latin-300-normal-Dwkm-oZ7.woff +0 -0
- package/gui/dist/assets/fira-sans-latin-400-normal-66xCkunx.woff +0 -0
- package/gui/dist/assets/fira-sans-latin-400-normal-a0AnQzuD.woff2 +0 -0
- package/gui/dist/assets/fira-sans-latin-500-normal-Bkfzcwmr.woff +0 -0
- package/gui/dist/assets/fira-sans-latin-500-normal-Bvbxc8ch.woff2 +0 -0
- package/gui/dist/assets/fira-sans-latin-600-normal-CXKlxLG9.woff2 +0 -0
- package/gui/dist/assets/fira-sans-latin-600-normal-DtSgd811.woff +0 -0
- package/gui/dist/assets/fira-sans-latin-700-normal-CRhwpWTq.woff2 +0 -0
- package/gui/dist/assets/fira-sans-latin-700-normal-DgqLm3Fi.woff +0 -0
- package/gui/dist/assets/fira-sans-latin-ext-300-normal-CAROBYR0.woff +0 -0
- package/gui/dist/assets/fira-sans-latin-ext-300-normal-GNF4Ac3G.woff2 +0 -0
- package/gui/dist/assets/fira-sans-latin-ext-400-normal-BCUwhuGB.woff +0 -0
- package/gui/dist/assets/fira-sans-latin-ext-400-normal-BLG_mAEf.woff2 +0 -0
- package/gui/dist/assets/fira-sans-latin-ext-500-normal-CFRf4jcC.woff +0 -0
- package/gui/dist/assets/fira-sans-latin-ext-500-normal-OO1QnNpY.woff2 +0 -0
- package/gui/dist/assets/fira-sans-latin-ext-600-normal-DLMQsaV4.woff +0 -0
- package/gui/dist/assets/fira-sans-latin-ext-600-normal-DTQBTt1m.woff2 +0 -0
- package/gui/dist/assets/fira-sans-latin-ext-700-normal-cfTxGFzJ.woff +0 -0
- package/gui/dist/assets/fira-sans-latin-ext-700-normal-g2Yrd4J5.woff2 +0 -0
- package/gui/dist/assets/fira-sans-vietnamese-300-normal-BO7He0nF.woff2 +0 -0
- package/gui/dist/assets/fira-sans-vietnamese-300-normal-CeyOCBN-.woff +0 -0
- package/gui/dist/assets/fira-sans-vietnamese-400-normal-DUVG35gE.woff +0 -0
- package/gui/dist/assets/fira-sans-vietnamese-400-normal-ljDTQFsx.woff2 +0 -0
- package/gui/dist/assets/fira-sans-vietnamese-500-normal-ChEXCnJy.woff2 +0 -0
- package/gui/dist/assets/fira-sans-vietnamese-500-normal-UV7M6ot_.woff +0 -0
- package/gui/dist/assets/fira-sans-vietnamese-600-normal-Bxjaclsw.woff +0 -0
- package/gui/dist/assets/fira-sans-vietnamese-600-normal-Dh2KAvU8.woff2 +0 -0
- package/gui/dist/assets/fira-sans-vietnamese-700-normal-DFi88MGY.woff +0 -0
- package/gui/dist/assets/fira-sans-vietnamese-700-normal-j5oLGLJY.woff2 +0 -0
- package/gui/dist/assets/index-CwQzep9r.js +413 -0
- package/gui/dist/assets/index-DUFjnEFT.css +1 -0
- package/gui/dist/assets/theme-BnuhLJ6X.css +1 -0
- package/gui/dist/assets/theme-G7EkTZY2.js +30 -0
- package/gui/dist/index.html +13 -0
- package/package.json +62 -0
|
@@ -0,0 +1,906 @@
|
|
|
1
|
+
import { cleanLines, extractProgramIds, matchHeader } from './source-lines.js';
|
|
2
|
+
/**
|
|
3
|
+
* Where-used por campo: para cada data-name del esquema, dónde se LEE y
|
|
4
|
+
* dónde se ESCRIBE en la PROCEDURE DIVISION, anclado a párrafo y línea.
|
|
5
|
+
*
|
|
6
|
+
* Misma disciplina que el resto del motor (ADR-0003): se reconoce un
|
|
7
|
+
* subconjunto de verbos con reparto de roles bien definido (MOVE, COMPUTE,
|
|
8
|
+
* la aritmética, SET, INITIALIZE, STRING/UNSTRING, INSPECT, READ/RETURN
|
|
9
|
+
* INTO, WRITE/REWRITE FROM, PERFORM VARYING, los contextos de condición) y
|
|
10
|
+
* los host variables de EXEC SQL/CICS. Todo lo demás se MARCA, no se
|
|
11
|
+
* inventa:
|
|
12
|
+
* - un argumento BY REFERENCE de una CALL, un MOVE CORRESPONDING, un
|
|
13
|
+
* host variable de SQL dinámico o un homónimo del esquema → `uncertain`;
|
|
14
|
+
* - una ocurrencia real de un campo en un verbo que el motor no modela
|
|
15
|
+
* todavía → `unclassified` (se toca ahí, pero no se afirma el rol);
|
|
16
|
+
* - un nombre en posición de operando que no casa con el esquema →
|
|
17
|
+
* `unknownNames` (puede estar en un copybook ausente).
|
|
18
|
+
*
|
|
19
|
+
* El rol se decide por la SENTENCIA completa, no por la línea: una MOVE
|
|
20
|
+
* puede repartirse en varias líneas y el operando destino está tras el TO.
|
|
21
|
+
* Por eso se acumula el texto hasta el punto y se trocea por verbos, con
|
|
22
|
+
* un mapa offset→línea para anclar cada token a su línea real del fuente.
|
|
23
|
+
*/
|
|
24
|
+
export function collectReferences(source, data) {
|
|
25
|
+
const cleaned = cleanLines(source);
|
|
26
|
+
const procIdx = cleaned.findIndex(l => PROCEDURE_HDR_RE.test(l.masked));
|
|
27
|
+
const fragment = procIdx === -1;
|
|
28
|
+
// Sin esquema no hay contra qué resolver los nombres: se devuelve vacío
|
|
29
|
+
// (la GUI siempre tiene `data` cuando tiene fuente).
|
|
30
|
+
if (!data || data.records.length === 0) {
|
|
31
|
+
return { fields: [], assignments: [], unknownNames: [], fragment };
|
|
32
|
+
}
|
|
33
|
+
// ── Índice del esquema: nombres conocidos, mapa 88→padre y homónimos ──
|
|
34
|
+
const nameCounts = new Map();
|
|
35
|
+
const parentOf88 = new Map();
|
|
36
|
+
const bump = (u) => {
|
|
37
|
+
nameCounts.set(u, (nameCounts.get(u) ?? 0) + 1);
|
|
38
|
+
};
|
|
39
|
+
const walk = (f) => {
|
|
40
|
+
if (f.type !== 'unresolved-copy') {
|
|
41
|
+
const u = f.name.toUpperCase();
|
|
42
|
+
bump(u);
|
|
43
|
+
for (const cv of f.conditionValues ?? [])
|
|
44
|
+
parentOf88.set(cv.name.toUpperCase(), u);
|
|
45
|
+
for (const g of f.renamesGroups ?? [])
|
|
46
|
+
bump(g.name.toUpperCase());
|
|
47
|
+
}
|
|
48
|
+
for (const c of f.children)
|
|
49
|
+
walk(c);
|
|
50
|
+
};
|
|
51
|
+
for (const r of data.records)
|
|
52
|
+
walk(r);
|
|
53
|
+
const known = new Set(nameCounts.keys());
|
|
54
|
+
const ambiguousNames = new Set([...nameCounts].filter(([, n]) => n > 1).map(([k]) => k));
|
|
55
|
+
// Nombres declarados con `INDEXED BY`: son registros especiales de
|
|
56
|
+
// COBOL, no campos del esquema (sin PIC ni offset), así que no están en
|
|
57
|
+
// `known`. Se recogen para no acusarlos como nombres desconocidos ni
|
|
58
|
+
// fabricarles referencias — igual que un `SET IX UP BY 1` no es una
|
|
59
|
+
// escritura de un dato.
|
|
60
|
+
const indexNames = new Set();
|
|
61
|
+
for (const l of cleaned) {
|
|
62
|
+
for (const m of l.masked.matchAll(/(?<![\w-])INDEXED\s+BY\s+((?:[A-Za-z][\w-]*\s*)+)/gi)) {
|
|
63
|
+
for (const n of m[1].trim().split(/\s+/))
|
|
64
|
+
indexNames.add(n.toUpperCase());
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/** Resuelve un token a un campo del esquema (o a su padre, si es un 88). */
|
|
68
|
+
const resolve = (u) => {
|
|
69
|
+
if (known.has(u))
|
|
70
|
+
return { name: u, ambiguous: ambiguousNames.has(u) };
|
|
71
|
+
const parent = parentOf88.get(u);
|
|
72
|
+
if (parent)
|
|
73
|
+
return { name: parent, via88: u, ambiguous: ambiguousNames.has(parent) };
|
|
74
|
+
return undefined;
|
|
75
|
+
};
|
|
76
|
+
const unknownNames = new Set();
|
|
77
|
+
const all = [];
|
|
78
|
+
const assignments = [];
|
|
79
|
+
const dedup = new Set();
|
|
80
|
+
// Estado del recorrido, mutado por el bucle principal.
|
|
81
|
+
const ids = extractProgramIds(cleaned);
|
|
82
|
+
const implicitEntry = ids[0]?.name ?? 'MAIN';
|
|
83
|
+
const nestedFrom = ids[1]?.line;
|
|
84
|
+
let paragraph;
|
|
85
|
+
let inProcedure = false;
|
|
86
|
+
let procHeaderOpen = false;
|
|
87
|
+
// Fragmento puro (sin ninguna cabecera de división): se analiza entero
|
|
88
|
+
// como cuerpo de PROCEDURE. Si hay OTRA división pero no PROCEDURE, no
|
|
89
|
+
// hay nada que analizar (no es un fragmento de procedimiento).
|
|
90
|
+
if (procIdx === -1 && !cleaned.some(l => OTHER_DIV_RE.test(l.masked)))
|
|
91
|
+
inProcedure = true;
|
|
92
|
+
/** Un token resuelto como una ocurrencia, o undefined si no casa. */
|
|
93
|
+
const oneRef = (token, kind, offset, uncertain = false) => {
|
|
94
|
+
const r = resolve(token.toUpperCase());
|
|
95
|
+
if (!r)
|
|
96
|
+
return undefined;
|
|
97
|
+
return {
|
|
98
|
+
name: r.name,
|
|
99
|
+
kind,
|
|
100
|
+
offset,
|
|
101
|
+
...(uncertain || r.ambiguous ? { uncertain: true } : {}),
|
|
102
|
+
...(r.via88 ? { via88: r.via88 } : {}),
|
|
103
|
+
...(r.ambiguous ? { ambiguous: true } : {}),
|
|
104
|
+
};
|
|
105
|
+
};
|
|
106
|
+
/**
|
|
107
|
+
* Todos los identificadores de `text` que resuelven a un campo, como
|
|
108
|
+
* ocurrencias de `kind`. Los que no resuelven y parecen un nombre se
|
|
109
|
+
* apuntan en `unknownNames` (salvo que `collectUnknown` sea false: los
|
|
110
|
+
* verbos no modelados no deben ensuciar esa lista).
|
|
111
|
+
*
|
|
112
|
+
* Lo que va entre paréntesis —subíndice (`WS-EL(WS-I)`) o longitud de
|
|
113
|
+
* modificación de referencia (`WS-X(1:WS-L)`)— NO es el operando en sí:
|
|
114
|
+
* son índices/longitudes que se LEEN. Se extraen aparte, marcados
|
|
115
|
+
* `subscript`, para que `MOVE A TO B(I)` cuente como escritura de B y
|
|
116
|
+
* lectura de I (antes I se contaba como escritura).
|
|
117
|
+
*/
|
|
118
|
+
const refsIn = (text, base, kind, opts = {}) => {
|
|
119
|
+
const out = [];
|
|
120
|
+
// Identificadores FUERA de paréntesis → rol `kind`.
|
|
121
|
+
for (const m of maskParens(text).matchAll(IDENT_RE)) {
|
|
122
|
+
const tok = m[0];
|
|
123
|
+
const u = tok.toUpperCase();
|
|
124
|
+
if (NON_NAME.has(u))
|
|
125
|
+
continue;
|
|
126
|
+
const at = base + (m.index ?? 0);
|
|
127
|
+
const o = oneRef(tok, kind, at, opts.uncertain ?? false);
|
|
128
|
+
if (o)
|
|
129
|
+
out.push(o);
|
|
130
|
+
else if (opts.collectUnknown !== false && tok.length > 1 && !indexNames.has(u))
|
|
131
|
+
unknownNames.add(u);
|
|
132
|
+
}
|
|
133
|
+
// Identificadores DENTRO de paréntesis → siempre lectura, `subscript`.
|
|
134
|
+
for (const [lo, hi] of parenRanges(text)) {
|
|
135
|
+
for (const m of text.slice(lo, hi).matchAll(IDENT_RE)) {
|
|
136
|
+
const tok = m[0];
|
|
137
|
+
if (NON_NAME.has(tok.toUpperCase()))
|
|
138
|
+
continue;
|
|
139
|
+
const o = oneRef(tok, 'read', base + lo + (m.index ?? 0), opts.uncertain ?? false);
|
|
140
|
+
if (o) {
|
|
141
|
+
o.subscript = true;
|
|
142
|
+
out.push(o);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return out;
|
|
147
|
+
};
|
|
148
|
+
// ── Handlers por verbo — cada uno devuelve las ocurrencias del chunk ──
|
|
149
|
+
// `chunk` es el texto enmascarado de la sentencia; `base` su offset
|
|
150
|
+
// absoluto en el buffer. Las funciones de recorte trabajan sobre el
|
|
151
|
+
// enmascarado (literales en blanco) para no confundir un `TO` dentro de
|
|
152
|
+
// una cadena con la palabra clave.
|
|
153
|
+
const moveRefs = (chunk, base) => {
|
|
154
|
+
const corr = CORRESPONDING_RE.test(chunk);
|
|
155
|
+
const toI = kw(chunk, 'TO');
|
|
156
|
+
if (toI < 0)
|
|
157
|
+
return refsIn(chunk, base, 'unclassified', { collectUnknown: false });
|
|
158
|
+
const opt = corr ? { uncertain: true } : {};
|
|
159
|
+
return [
|
|
160
|
+
...refsIn(chunk.slice(0, toI), base, 'read', opt),
|
|
161
|
+
...refsIn(chunk.slice(toI + 2), base + toI + 2, 'write', opt),
|
|
162
|
+
];
|
|
163
|
+
};
|
|
164
|
+
const computeRefs = (chunk, base) => {
|
|
165
|
+
const eqI = chunk.indexOf('=');
|
|
166
|
+
if (eqI < 0)
|
|
167
|
+
return refsIn(chunk, base, 'unclassified', { collectUnknown: false });
|
|
168
|
+
return [
|
|
169
|
+
...refsIn(chunk.slice(0, eqI), base, 'write'),
|
|
170
|
+
...refsIn(chunk.slice(eqI + 1), base + eqI + 1, 'read'),
|
|
171
|
+
];
|
|
172
|
+
};
|
|
173
|
+
const arithRefs = (verb, chunk, base) => {
|
|
174
|
+
const linkKws = verb === 'ADD' ? ['TO'] : verb === 'SUBTRACT' ? ['FROM'] : verb === 'MULTIPLY' ? ['BY'] : ['INTO', 'BY'];
|
|
175
|
+
const linkI = kwAny(chunk, linkKws);
|
|
176
|
+
const givI = kw(chunk, 'GIVING');
|
|
177
|
+
const remI = kw(chunk, 'REMAINDER');
|
|
178
|
+
if (givI >= 0) {
|
|
179
|
+
// Todo lo anterior a GIVING (fuentes + el operando TO/FROM/BY/INTO)
|
|
180
|
+
// se lee; el/los operando(s) tras GIVING (y REMAINDER) se escriben.
|
|
181
|
+
const out = refsIn(chunk.slice(0, givI), base, 'read');
|
|
182
|
+
const wEnd = remI > givI ? remI : chunk.length;
|
|
183
|
+
out.push(...refsIn(chunk.slice(givI + 6, wEnd), base + givI + 6, 'write'));
|
|
184
|
+
if (remI >= 0)
|
|
185
|
+
out.push(...refsIn(chunk.slice(remI + 9), base + remI + 9, 'write'));
|
|
186
|
+
return out;
|
|
187
|
+
}
|
|
188
|
+
if (linkI >= 0) {
|
|
189
|
+
const linkLen = /^[A-Za-z]+/.exec(chunk.slice(linkI))?.[0].length ?? 2;
|
|
190
|
+
// Sin GIVING, el operando tras TO/FROM/BY/INTO se lee Y se escribe.
|
|
191
|
+
return [
|
|
192
|
+
...refsIn(chunk.slice(0, linkI), base, 'read'),
|
|
193
|
+
...refsIn(chunk.slice(linkI + linkLen), base + linkI + linkLen, 'read-write'),
|
|
194
|
+
];
|
|
195
|
+
}
|
|
196
|
+
return refsIn(chunk, base, 'unclassified', { collectUnknown: false });
|
|
197
|
+
};
|
|
198
|
+
const setRefs = (chunk, base) => {
|
|
199
|
+
const ud = /(?<![\w-])(?:UP|DOWN)\s+BY(?![\w-])/i.exec(chunk);
|
|
200
|
+
if (ud) {
|
|
201
|
+
const byEnd = ud.index + ud[0].length;
|
|
202
|
+
return [
|
|
203
|
+
...refsIn(chunk.slice(0, ud.index), base, 'read-write'),
|
|
204
|
+
...refsIn(chunk.slice(byEnd), base + byEnd, 'read'),
|
|
205
|
+
];
|
|
206
|
+
}
|
|
207
|
+
const toI = kw(chunk, 'TO');
|
|
208
|
+
if (toI < 0)
|
|
209
|
+
return refsIn(chunk, base, 'unclassified', { collectUnknown: false });
|
|
210
|
+
const tgt = refsIn(chunk.slice(0, toI), base, 'write');
|
|
211
|
+
const val = chunk.slice(toI + 2);
|
|
212
|
+
// SET <88> TO TRUE/FALSE: el 88 resuelve a su campo padre (write via88);
|
|
213
|
+
// no hay valor legible que leer.
|
|
214
|
+
if (/(?<![\w-])(?:TRUE|FALSE)(?![\w-])/i.test(val))
|
|
215
|
+
return tgt;
|
|
216
|
+
return [...tgt, ...refsIn(val, base + toI + 2, 'read')];
|
|
217
|
+
};
|
|
218
|
+
const initRefs = (chunk, base) => {
|
|
219
|
+
const rI = kw(chunk, 'REPLACING');
|
|
220
|
+
const out = refsIn(chunk.slice(0, rI >= 0 ? rI : chunk.length), base, 'write');
|
|
221
|
+
if (rI >= 0)
|
|
222
|
+
out.push(...refsIn(chunk.slice(rI), base + rI, 'read'));
|
|
223
|
+
return out;
|
|
224
|
+
};
|
|
225
|
+
const sideRefs = (chunk, base, stopKw, kind) => {
|
|
226
|
+
const i = kw(chunk, stopKw);
|
|
227
|
+
return refsIn(i >= 0 ? chunk.slice(0, i) : chunk, base, kind);
|
|
228
|
+
};
|
|
229
|
+
/** Marca ocurrencias como auxiliares (POINTER/TALLYING): fuera de aristas. */
|
|
230
|
+
const markAux = (raws) => {
|
|
231
|
+
for (const r of raws)
|
|
232
|
+
r.aux = true;
|
|
233
|
+
return raws;
|
|
234
|
+
};
|
|
235
|
+
const stringRefs = (chunk, base) => {
|
|
236
|
+
const intoI = kw(chunk, 'INTO');
|
|
237
|
+
if (intoI < 0)
|
|
238
|
+
return refsIn(chunk, base, 'unclassified', { collectUnknown: false });
|
|
239
|
+
const out = refsIn(chunk.slice(0, intoI), base, 'read'); // fuentes + operando DELIMITED BY
|
|
240
|
+
const rest = chunk.slice(intoI + 4);
|
|
241
|
+
const rb = base + intoI + 4;
|
|
242
|
+
const ptrI = kw(rest, 'POINTER');
|
|
243
|
+
const ovI = kwAny(rest, ['ON', 'NOT', 'END-STRING']);
|
|
244
|
+
const wEnd = ptrI >= 0 ? ptrI : ovI >= 0 ? ovI : rest.length;
|
|
245
|
+
out.push(...refsIn(rest.slice(0, wEnd), rb, 'write'));
|
|
246
|
+
if (ptrI >= 0) {
|
|
247
|
+
const pEnd = ovI > ptrI ? ovI : rest.length;
|
|
248
|
+
out.push(...markAux(refsIn(rest.slice(ptrI + 7, pEnd), rb + ptrI + 7, 'read-write')));
|
|
249
|
+
}
|
|
250
|
+
return out;
|
|
251
|
+
};
|
|
252
|
+
const unstringRefs = (chunk, base) => {
|
|
253
|
+
const intoI = kw(chunk, 'INTO');
|
|
254
|
+
if (intoI < 0)
|
|
255
|
+
return refsIn(chunk, base, 'unclassified', { collectUnknown: false });
|
|
256
|
+
const out = refsIn(chunk.slice(0, intoI), base, 'read'); // UNSTRING <origen> [DELIMITED BY <d>]
|
|
257
|
+
const rest = chunk.slice(intoI + 4);
|
|
258
|
+
const rb = base + intoI + 4;
|
|
259
|
+
const ptrI = kw(rest, 'POINTER');
|
|
260
|
+
const endI = kwAny(rest, ['ON', 'NOT', 'END-UNSTRING']);
|
|
261
|
+
const wEnd = ptrI >= 0 ? ptrI : endI >= 0 ? endI : rest.length;
|
|
262
|
+
// destinos + operandos de DELIMITER IN / COUNT IN / TALLYING IN
|
|
263
|
+
out.push(...refsIn(rest.slice(0, wEnd), rb, 'write'));
|
|
264
|
+
if (ptrI >= 0) {
|
|
265
|
+
const seg = rest.slice(ptrI + 7, endI > ptrI ? endI : rest.length);
|
|
266
|
+
const tI = kw(seg, 'TALLYING');
|
|
267
|
+
if (tI >= 0) {
|
|
268
|
+
out.push(...markAux(refsIn(seg.slice(0, tI), rb + ptrI + 7, 'read-write')));
|
|
269
|
+
out.push(...markAux(refsIn(seg.slice(tI), rb + ptrI + 7 + tI, 'write')));
|
|
270
|
+
}
|
|
271
|
+
else {
|
|
272
|
+
out.push(...markAux(refsIn(seg, rb + ptrI + 7, 'read-write')));
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
return out;
|
|
276
|
+
};
|
|
277
|
+
const inspectRefs = (chunk, base) => {
|
|
278
|
+
const opI = kwAny(chunk, ['TALLYING', 'REPLACING', 'CONVERTING']);
|
|
279
|
+
const modifies = /(?<![\w-])(?:REPLACING|CONVERTING)(?![\w-])/i.test(chunk);
|
|
280
|
+
// INSPECT <x> ... — INSPECT queda fuera (NON_NAME); x se lee, y además
|
|
281
|
+
// se escribe si hay REPLACING/CONVERTING.
|
|
282
|
+
const out = refsIn(chunk.slice(0, opI >= 0 ? opI : chunk.length), base, modifies ? 'read-write' : 'read');
|
|
283
|
+
if (opI < 0)
|
|
284
|
+
return out;
|
|
285
|
+
const rest = chunk.slice(opI);
|
|
286
|
+
const rb = base + opI;
|
|
287
|
+
if (/^\s*TALLYING(?![\w-])/i.test(rest)) {
|
|
288
|
+
const forI = kw(rest, 'FOR');
|
|
289
|
+
out.push(...refsIn(rest.slice(0, forI >= 0 ? forI : rest.length), rb, 'write')); // contador
|
|
290
|
+
if (forI >= 0)
|
|
291
|
+
out.push(...refsIn(rest.slice(forI), rb + forI, 'read')); // ... BEFORE/AFTER INITIAL <x>
|
|
292
|
+
}
|
|
293
|
+
else {
|
|
294
|
+
out.push(...refsIn(rest, rb, 'read')); // REPLACING/CONVERTING ... : operandos identificador se leen
|
|
295
|
+
}
|
|
296
|
+
return out;
|
|
297
|
+
};
|
|
298
|
+
const readRefs = (chunk, base) => {
|
|
299
|
+
const out = [];
|
|
300
|
+
const intoI = kw(chunk, 'INTO');
|
|
301
|
+
if (intoI >= 0) {
|
|
302
|
+
const seg = chunk.slice(intoI + 4);
|
|
303
|
+
const end = kwAny(seg, ['KEY', 'AT', 'INVALID', 'NOT', 'WITH', 'END-READ']);
|
|
304
|
+
out.push(...refsIn(seg.slice(0, end >= 0 ? end : seg.length), base + intoI + 4, 'write'));
|
|
305
|
+
}
|
|
306
|
+
const keyI = kw(chunk, 'KEY');
|
|
307
|
+
if (keyI >= 0) {
|
|
308
|
+
const seg = chunk.slice(keyI + 3);
|
|
309
|
+
const end = kwAny(seg, ['AT', 'INVALID', 'NOT', 'END-READ']);
|
|
310
|
+
out.push(...refsIn(seg.slice(0, end >= 0 ? end : seg.length), base + keyI + 3, 'read'));
|
|
311
|
+
}
|
|
312
|
+
return out;
|
|
313
|
+
};
|
|
314
|
+
const returnRefs = (chunk, base) => {
|
|
315
|
+
const intoI = kw(chunk, 'INTO');
|
|
316
|
+
if (intoI < 0)
|
|
317
|
+
return [];
|
|
318
|
+
const seg = chunk.slice(intoI + 4);
|
|
319
|
+
const end = kwAny(seg, ['AT', 'NOT', 'END-RETURN']);
|
|
320
|
+
return refsIn(seg.slice(0, end >= 0 ? end : seg.length), base + intoI + 4, 'write');
|
|
321
|
+
};
|
|
322
|
+
const writeRefs = (chunk, base) => {
|
|
323
|
+
const fromI = kw(chunk, 'FROM');
|
|
324
|
+
// Primer identificador tras WRITE/REWRITE/RELEASE = el registro → se escribe.
|
|
325
|
+
const out = refsIn(chunk.slice(0, fromI >= 0 ? fromI : chunk.length), base, 'write');
|
|
326
|
+
if (fromI >= 0) {
|
|
327
|
+
const seg = chunk.slice(fromI + 4);
|
|
328
|
+
const end = kwAny(seg, ['BEFORE', 'AFTER', 'AT', 'INVALID', 'NOT', 'END-WRITE', 'END-REWRITE']);
|
|
329
|
+
out.push(...refsIn(seg.slice(0, end >= 0 ? end : seg.length), base + fromI + 4, 'read'));
|
|
330
|
+
}
|
|
331
|
+
return out;
|
|
332
|
+
};
|
|
333
|
+
const performRefs = (chunk, base) => {
|
|
334
|
+
const out = [];
|
|
335
|
+
const vI = kw(chunk, 'VARYING');
|
|
336
|
+
if (vI >= 0) {
|
|
337
|
+
const seg = chunk.slice(vI);
|
|
338
|
+
const sb = base + vI;
|
|
339
|
+
for (const m of seg.matchAll(/(?<![\w-])(?:VARYING|AFTER)\s+([A-Za-z][\w-]*)/gi)) {
|
|
340
|
+
const nameStart = (m.index ?? 0) + m[0].length - m[1].length;
|
|
341
|
+
const o = oneRef(m[1], 'write', sb + nameStart);
|
|
342
|
+
if (o)
|
|
343
|
+
out.push(o);
|
|
344
|
+
}
|
|
345
|
+
for (const m of seg.matchAll(/(?<![\w-])(?:FROM|BY)\s+([A-Za-z][\w-]*)/gi)) {
|
|
346
|
+
const nameStart = (m.index ?? 0) + m[0].length - m[1].length;
|
|
347
|
+
const o = oneRef(m[1], 'read', sb + nameStart);
|
|
348
|
+
if (o)
|
|
349
|
+
out.push(o);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
const uI = kw(chunk, 'UNTIL');
|
|
353
|
+
if (uI >= 0) {
|
|
354
|
+
const seg = chunk.slice(uI + 5);
|
|
355
|
+
const end = kw(seg, 'END-PERFORM');
|
|
356
|
+
out.push(...refsIn(seg.slice(0, end >= 0 ? end : seg.length), base + uI + 5, 'read'));
|
|
357
|
+
}
|
|
358
|
+
const tm = /(?<![\w-])([A-Za-z][\w-]*)\s+TIMES(?![\w-])/i.exec(chunk);
|
|
359
|
+
if (tm) {
|
|
360
|
+
const o = oneRef(tm[1], 'read', base + (tm.index ?? 0));
|
|
361
|
+
if (o)
|
|
362
|
+
out.push(o);
|
|
363
|
+
}
|
|
364
|
+
return out;
|
|
365
|
+
};
|
|
366
|
+
const callRefs = (chunk, base) => {
|
|
367
|
+
const out = [];
|
|
368
|
+
const uI = kw(chunk, 'USING');
|
|
369
|
+
const rI = kw(chunk, 'RETURNING');
|
|
370
|
+
if (uI >= 0) {
|
|
371
|
+
const argPart = chunk.slice(uI + 5, rI > uI ? rI : chunk.length);
|
|
372
|
+
const ab = base + uI + 5;
|
|
373
|
+
// BY REFERENCE (por defecto) → el llamado puede devolver valor:
|
|
374
|
+
// read-write + uncertain. BY CONTENT/VALUE → solo lectura.
|
|
375
|
+
let byRef = true;
|
|
376
|
+
for (const m of argPart.matchAll(/(?<![\w-])(?:BY\s+(REFERENCE|CONTENT|VALUE)|([A-Za-z][\w-]*))(?![\w-])/gi)) {
|
|
377
|
+
if (m[1]) {
|
|
378
|
+
byRef = m[1].toUpperCase() === 'REFERENCE';
|
|
379
|
+
continue;
|
|
380
|
+
}
|
|
381
|
+
const tok = m[2];
|
|
382
|
+
if (!tok || NON_NAME.has(tok.toUpperCase()))
|
|
383
|
+
continue;
|
|
384
|
+
const o = oneRef(tok, byRef ? 'read-write' : 'read', ab + (m.index ?? 0), byRef);
|
|
385
|
+
if (o)
|
|
386
|
+
out.push(o);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
if (rI >= 0) {
|
|
390
|
+
const seg = chunk.slice(rI + 9);
|
|
391
|
+
const mm = /^\s*([A-Za-z][\w-]*)/.exec(seg);
|
|
392
|
+
if (mm) {
|
|
393
|
+
const o = oneRef(mm[1], 'write', base + rI + 9 + mm[0].length - mm[1].length);
|
|
394
|
+
if (o)
|
|
395
|
+
out.push(o);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
return out;
|
|
399
|
+
};
|
|
400
|
+
const goRefs = (chunk, base) => {
|
|
401
|
+
const m = /(?<![\w-])DEPENDING\s+ON\s+([A-Za-z][\w-]*)/i.exec(chunk);
|
|
402
|
+
if (!m)
|
|
403
|
+
return [];
|
|
404
|
+
const o = oneRef(m[1], 'read', base + (m.index ?? 0) + m[0].length - m[1].length);
|
|
405
|
+
return o ? [o] : [];
|
|
406
|
+
};
|
|
407
|
+
const dispatch = (verb, chunk, base) => {
|
|
408
|
+
switch (verb) {
|
|
409
|
+
case 'MOVE':
|
|
410
|
+
return moveRefs(chunk, base);
|
|
411
|
+
case 'COMPUTE':
|
|
412
|
+
return computeRefs(chunk, base);
|
|
413
|
+
case 'ADD':
|
|
414
|
+
case 'SUBTRACT':
|
|
415
|
+
case 'MULTIPLY':
|
|
416
|
+
case 'DIVIDE':
|
|
417
|
+
return arithRefs(verb, chunk, base);
|
|
418
|
+
case 'SET':
|
|
419
|
+
return setRefs(chunk, base);
|
|
420
|
+
case 'INITIALIZE':
|
|
421
|
+
return initRefs(chunk, base);
|
|
422
|
+
case 'ACCEPT':
|
|
423
|
+
return sideRefs(chunk, base, 'FROM', 'write');
|
|
424
|
+
case 'DISPLAY':
|
|
425
|
+
return sideRefs(chunk, base, 'UPON', 'read');
|
|
426
|
+
case 'STRING':
|
|
427
|
+
return stringRefs(chunk, base);
|
|
428
|
+
case 'UNSTRING':
|
|
429
|
+
return unstringRefs(chunk, base);
|
|
430
|
+
case 'INSPECT':
|
|
431
|
+
return inspectRefs(chunk, base);
|
|
432
|
+
case 'READ':
|
|
433
|
+
return readRefs(chunk, base);
|
|
434
|
+
case 'RETURN':
|
|
435
|
+
return returnRefs(chunk, base);
|
|
436
|
+
case 'WRITE':
|
|
437
|
+
case 'REWRITE':
|
|
438
|
+
case 'RELEASE':
|
|
439
|
+
return writeRefs(chunk, base);
|
|
440
|
+
case 'PERFORM':
|
|
441
|
+
return performRefs(chunk, base);
|
|
442
|
+
case 'IF':
|
|
443
|
+
case 'EVALUATE':
|
|
444
|
+
case 'WHEN':
|
|
445
|
+
return refsIn(chunk, base, 'read');
|
|
446
|
+
case 'CALL':
|
|
447
|
+
return callRefs(chunk, base);
|
|
448
|
+
case 'GO':
|
|
449
|
+
return goRefs(chunk, base);
|
|
450
|
+
default:
|
|
451
|
+
return refsIn(chunk, base, 'unclassified', { collectUnknown: false });
|
|
452
|
+
}
|
|
453
|
+
};
|
|
454
|
+
// ── Emisión de referencias ──────────────────────────────────────────
|
|
455
|
+
const emitRef = (o, verb, snippet, line) => {
|
|
456
|
+
const fr = {
|
|
457
|
+
name: o.name,
|
|
458
|
+
kind: o.kind,
|
|
459
|
+
verb,
|
|
460
|
+
paragraph: paragraph ?? implicitEntry,
|
|
461
|
+
line,
|
|
462
|
+
snippet,
|
|
463
|
+
...(paragraph === undefined ? { paragraphImplicit: true } : {}),
|
|
464
|
+
...(o.uncertain ? { uncertain: true } : {}),
|
|
465
|
+
...(o.via88 ? { via88: o.via88 } : {}),
|
|
466
|
+
...(o.ambiguous ? { ambiguous: true } : {}),
|
|
467
|
+
};
|
|
468
|
+
const key = `${fr.name}|${fr.kind}|${fr.line}|${fr.verb}`;
|
|
469
|
+
if (dedup.has(key))
|
|
470
|
+
return;
|
|
471
|
+
dedup.add(key);
|
|
472
|
+
all.push(fr);
|
|
473
|
+
};
|
|
474
|
+
const makeLineAt = (map) => (offset) => {
|
|
475
|
+
let ln = map[0]?.line ?? 0;
|
|
476
|
+
for (const e of map) {
|
|
477
|
+
if (e.start <= offset)
|
|
478
|
+
ln = e.line;
|
|
479
|
+
else
|
|
480
|
+
break;
|
|
481
|
+
}
|
|
482
|
+
return ln;
|
|
483
|
+
};
|
|
484
|
+
/**
|
|
485
|
+
* Aristas de propagación de valor de una sentencia: cada operando fuente
|
|
486
|
+
* (lectura, salvo subíndices y auxiliares) fluye a cada operando destino
|
|
487
|
+
* (escritura, salvo auxiliares). Solo para verbos que COPIAN o DERIVAN un
|
|
488
|
+
* valor (`EDGE_VERBS`) — no IF/EVALUATE (condición), READ (origen externo,
|
|
489
|
+
* el fichero) ni PERFORM.
|
|
490
|
+
*/
|
|
491
|
+
const emitEdges = (verb, label, occs, snippet, lineAt) => {
|
|
492
|
+
if (!EDGE_VERBS.has(verb))
|
|
493
|
+
return;
|
|
494
|
+
const sources = occs.filter(o => (o.kind === 'read' || o.kind === 'read-write') && !o.subscript && !o.aux);
|
|
495
|
+
const sinks = occs.filter(o => (o.kind === 'write' || o.kind === 'read-write') && !o.aux);
|
|
496
|
+
for (const s of sources) {
|
|
497
|
+
for (const t of sinks) {
|
|
498
|
+
if (s.name === t.name)
|
|
499
|
+
continue;
|
|
500
|
+
assignments.push({
|
|
501
|
+
from: s.name,
|
|
502
|
+
to: t.name,
|
|
503
|
+
verb: label,
|
|
504
|
+
paragraph: paragraph ?? implicitEntry,
|
|
505
|
+
line: lineAt(t.offset),
|
|
506
|
+
snippet,
|
|
507
|
+
kind: 'statement',
|
|
508
|
+
...(paragraph === undefined ? { paragraphImplicit: true } : {}),
|
|
509
|
+
...(s.uncertain || t.uncertain ? { uncertain: true } : {}),
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
};
|
|
514
|
+
const processSentence = (masked, body, map) => {
|
|
515
|
+
const lineAt = makeLineAt(map);
|
|
516
|
+
const marks = [];
|
|
517
|
+
for (const m of masked.matchAll(VERB_SPLIT_RE))
|
|
518
|
+
marks.push({ at: m.index ?? 0, verb: m[1].toUpperCase() });
|
|
519
|
+
for (let i = 0; i < marks.length; i++) {
|
|
520
|
+
const start = marks[i].at;
|
|
521
|
+
const end = i + 1 < marks.length ? marks[i + 1].at : masked.length;
|
|
522
|
+
const verb = marks[i].verb;
|
|
523
|
+
const occs = dispatch(verb, masked.slice(start, end), start);
|
|
524
|
+
if (occs.length === 0)
|
|
525
|
+
continue;
|
|
526
|
+
const snippet = collapse(body.slice(start, end));
|
|
527
|
+
const label = verb === 'GO' ? 'GO TO' : verb;
|
|
528
|
+
for (const o of occs)
|
|
529
|
+
emitRef(o, label, snippet, lineAt(o.offset));
|
|
530
|
+
emitEdges(verb, label, occs, snippet, lineAt);
|
|
531
|
+
}
|
|
532
|
+
};
|
|
533
|
+
// ── EXEC SQL / CICS ─────────────────────────────────────────────────
|
|
534
|
+
const sqlRefs = (masked, base) => {
|
|
535
|
+
const vm = /(?<![\w-])EXEC\s+SQL\s+([A-Za-z-]+)/i.exec(masked);
|
|
536
|
+
const verb = vm ? vm[1].toUpperCase() : '';
|
|
537
|
+
if (verb === 'INCLUDE')
|
|
538
|
+
return [];
|
|
539
|
+
const dynamic = verb === 'PREPARE' || verb === 'EXECUTE';
|
|
540
|
+
// Rangos de host-vars de un INTO (SELECT ... INTO / FETCH ... INTO),
|
|
541
|
+
// NO el "INSERT INTO <tabla>".
|
|
542
|
+
const intoRanges = [];
|
|
543
|
+
for (const m of masked.matchAll(/(?<![\w-])INTO(?![\w-])/gi)) {
|
|
544
|
+
const idx = m.index ?? 0;
|
|
545
|
+
if (/INSERT\s*$/i.test(masked.slice(Math.max(0, idx - 12), idx)))
|
|
546
|
+
continue;
|
|
547
|
+
const after = masked.slice(idx + 4);
|
|
548
|
+
const stop = /(?<![\w-])(?:FROM|WHERE|ORDER|GROUP|HAVING|FOR|FETCH|USING|SET|VALUES|END-EXEC)(?![\w-])/i.exec(after);
|
|
549
|
+
intoRanges.push([idx + 4, idx + 4 + (stop ? (stop.index ?? after.length) : after.length)]);
|
|
550
|
+
}
|
|
551
|
+
const out = [];
|
|
552
|
+
for (const m of masked.matchAll(/:([A-Za-z][A-Za-z0-9_-]*)\s*(?::\s*([A-Za-z][A-Za-z0-9_-]*))?/g)) {
|
|
553
|
+
const idx = m.index ?? 0;
|
|
554
|
+
const inInto = intoRanges.some(([s, e]) => idx >= s && idx < e);
|
|
555
|
+
const kind = inInto ? 'write' : 'read';
|
|
556
|
+
const host = oneRef(m[1], kind, base + idx + 1, dynamic);
|
|
557
|
+
if (host)
|
|
558
|
+
out.push(host);
|
|
559
|
+
if (m[2]) {
|
|
560
|
+
const ind = oneRef(m[2], kind, base + idx + m[0].lastIndexOf(m[2]), true); // variable indicadora
|
|
561
|
+
if (ind)
|
|
562
|
+
out.push(ind);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
return out;
|
|
566
|
+
};
|
|
567
|
+
const cicsRefs = (masked, base) => {
|
|
568
|
+
const out = [];
|
|
569
|
+
for (const m of masked.matchAll(/(?<![\w-])([A-Za-z][\w-]*)\s*\(\s*([A-Za-z][\w-]*)\s*\)/g)) {
|
|
570
|
+
const key = m[1].toUpperCase();
|
|
571
|
+
const op = m[2];
|
|
572
|
+
const offset = base + (m.index ?? 0) + m[0].lastIndexOf(op);
|
|
573
|
+
let kind;
|
|
574
|
+
let uncertain = false;
|
|
575
|
+
if (key === 'INTO' || key === 'SET')
|
|
576
|
+
kind = 'write';
|
|
577
|
+
else if (key === 'FROM')
|
|
578
|
+
kind = 'read';
|
|
579
|
+
else if (key === 'RESP' || key === 'RESP2')
|
|
580
|
+
kind = 'write';
|
|
581
|
+
else if (key === 'LENGTH' || key === 'FLENGTH' || key === 'KEYLENGTH') {
|
|
582
|
+
kind = 'read-write';
|
|
583
|
+
uncertain = true;
|
|
584
|
+
}
|
|
585
|
+
else
|
|
586
|
+
kind = 'read'; // FILE, DATASET, MAP, RIDFLD, QUEUE… con operando data-name: se lee
|
|
587
|
+
const o = oneRef(op, kind, offset, uncertain);
|
|
588
|
+
if (o)
|
|
589
|
+
out.push(o);
|
|
590
|
+
}
|
|
591
|
+
return out;
|
|
592
|
+
};
|
|
593
|
+
// ── Buffer de sentencia (se acumula hasta el punto) ─────────────────
|
|
594
|
+
let bufMasked = '';
|
|
595
|
+
let bufBody = '';
|
|
596
|
+
let bufMap = [];
|
|
597
|
+
const addLine = (l) => {
|
|
598
|
+
bufMap.push({ start: bufMasked.length, line: l.line });
|
|
599
|
+
bufMasked += l.masked + '\n';
|
|
600
|
+
bufBody += l.body + '\n';
|
|
601
|
+
};
|
|
602
|
+
const drainSentences = () => {
|
|
603
|
+
for (;;) {
|
|
604
|
+
const m = PERIOD_RE.exec(bufMasked);
|
|
605
|
+
if (!m)
|
|
606
|
+
return;
|
|
607
|
+
const cut = m.index;
|
|
608
|
+
processSentence(bufMasked.slice(0, cut), bufBody.slice(0, cut), bufMap);
|
|
609
|
+
const rem = cut + 1;
|
|
610
|
+
bufMasked = bufMasked.slice(rem);
|
|
611
|
+
bufBody = bufBody.slice(rem);
|
|
612
|
+
bufMap = bufMap.map(e => ({ start: Math.max(0, e.start - rem), line: e.line }));
|
|
613
|
+
}
|
|
614
|
+
};
|
|
615
|
+
const flushBuffer = () => {
|
|
616
|
+
if (bufMasked.trim() !== '')
|
|
617
|
+
processSentence(bufMasked, bufBody, bufMap);
|
|
618
|
+
bufMasked = '';
|
|
619
|
+
bufBody = '';
|
|
620
|
+
bufMap = [];
|
|
621
|
+
};
|
|
622
|
+
// ── Buffer de bloque EXEC ──────────────────────────────────────────
|
|
623
|
+
let execMasked = '';
|
|
624
|
+
let execBody = '';
|
|
625
|
+
let execMap = [];
|
|
626
|
+
let execOpen = false;
|
|
627
|
+
const addExecLine = (l) => {
|
|
628
|
+
execMap.push({ start: execMasked.length, line: l.line });
|
|
629
|
+
execMasked += l.masked + '\n';
|
|
630
|
+
execBody += l.body + '\n';
|
|
631
|
+
};
|
|
632
|
+
const finishExec = () => {
|
|
633
|
+
const isCics = /(?<![\w-])EXEC\s+CICS(?![\w-])/i.test(execMasked);
|
|
634
|
+
const occs = isCics ? cicsRefs(execMasked, 0) : sqlRefs(execMasked, 0);
|
|
635
|
+
if (occs.length > 0) {
|
|
636
|
+
const lineAt = makeLineAt(execMap);
|
|
637
|
+
const vm = /(?<![\w-])EXEC\s+SQL\s+([A-Za-z-]+)/i.exec(execMasked);
|
|
638
|
+
const label = isCics ? 'EXEC CICS' : vm ? `EXEC SQL ${vm[1].toUpperCase()}` : 'EXEC SQL';
|
|
639
|
+
const snippet = collapse(execBody);
|
|
640
|
+
for (const o of occs)
|
|
641
|
+
emitRef(o, label, snippet, lineAt(o.offset));
|
|
642
|
+
}
|
|
643
|
+
execMasked = '';
|
|
644
|
+
execBody = '';
|
|
645
|
+
execMap = [];
|
|
646
|
+
};
|
|
647
|
+
// ── Bucle principal ────────────────────────────────────────────────
|
|
648
|
+
for (const l of cleaned) {
|
|
649
|
+
if (END_PROGRAM_RE.test(l.masked))
|
|
650
|
+
break;
|
|
651
|
+
if (nestedFrom !== undefined && l.line >= nestedFrom)
|
|
652
|
+
break;
|
|
653
|
+
if (!inProcedure) {
|
|
654
|
+
if (PROCEDURE_HDR_RE.test(l.masked)) {
|
|
655
|
+
inProcedure = true;
|
|
656
|
+
// "PROCEDURE DIVISION [USING ...]." puede seguir en varias líneas
|
|
657
|
+
// hasta su punto: nada de eso son sentencias.
|
|
658
|
+
procHeaderOpen = !/\.\s*$/.test(l.masked);
|
|
659
|
+
}
|
|
660
|
+
continue;
|
|
661
|
+
}
|
|
662
|
+
if (procHeaderOpen) {
|
|
663
|
+
if (/\.\s*$/.test(l.masked))
|
|
664
|
+
procHeaderOpen = false;
|
|
665
|
+
continue;
|
|
666
|
+
}
|
|
667
|
+
if (execOpen) {
|
|
668
|
+
addExecLine(l);
|
|
669
|
+
if (END_EXEC_RE.test(l.masked)) {
|
|
670
|
+
finishExec();
|
|
671
|
+
execOpen = false;
|
|
672
|
+
}
|
|
673
|
+
continue;
|
|
674
|
+
}
|
|
675
|
+
const hdr = matchHeader(l.masked);
|
|
676
|
+
if (hdr) {
|
|
677
|
+
flushBuffer();
|
|
678
|
+
paragraph = hdr.name;
|
|
679
|
+
continue;
|
|
680
|
+
}
|
|
681
|
+
if (EXEC_START_RE.test(l.masked)) {
|
|
682
|
+
flushBuffer();
|
|
683
|
+
addExecLine(l);
|
|
684
|
+
if (END_EXEC_RE.test(l.masked))
|
|
685
|
+
finishExec();
|
|
686
|
+
else
|
|
687
|
+
execOpen = true;
|
|
688
|
+
continue;
|
|
689
|
+
}
|
|
690
|
+
addLine(l);
|
|
691
|
+
drainSentences();
|
|
692
|
+
}
|
|
693
|
+
flushBuffer();
|
|
694
|
+
// ── Agrupar por nombre ────────────────────────────────────────────
|
|
695
|
+
const byName = new Map();
|
|
696
|
+
for (const fr of all) {
|
|
697
|
+
let u = byName.get(fr.name);
|
|
698
|
+
if (!u) {
|
|
699
|
+
u = { name: fr.name, reads: [], writes: [], unclassified: [] };
|
|
700
|
+
byName.set(fr.name, u);
|
|
701
|
+
}
|
|
702
|
+
if (fr.kind === 'read')
|
|
703
|
+
u.reads.push(fr);
|
|
704
|
+
else if (fr.kind === 'write')
|
|
705
|
+
u.writes.push(fr);
|
|
706
|
+
else if (fr.kind === 'read-write') {
|
|
707
|
+
u.reads.push(fr);
|
|
708
|
+
u.writes.push(fr);
|
|
709
|
+
}
|
|
710
|
+
else
|
|
711
|
+
u.unclassified.push(fr);
|
|
712
|
+
}
|
|
713
|
+
const byLine = (a, b) => a.line - b.line;
|
|
714
|
+
const fields = [...byName.values()]
|
|
715
|
+
.map(u => ({
|
|
716
|
+
name: u.name,
|
|
717
|
+
reads: u.reads.sort(byLine),
|
|
718
|
+
writes: u.writes.sort(byLine),
|
|
719
|
+
unclassified: u.unclassified.sort(byLine),
|
|
720
|
+
}))
|
|
721
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
722
|
+
// Aristas de solape REDEFINES: escribir un alias cambia los bytes del
|
|
723
|
+
// otro, así que el valor se propaga en ambos sentidos. No es una
|
|
724
|
+
// asignación (es una reinterpretación de memoria) → `uncertain` + kind
|
|
725
|
+
// `redefines`, sin línea. El esquema ya da el dato (`SchemaField.redefines`).
|
|
726
|
+
const seenRedef = new Set();
|
|
727
|
+
const walkRedef = (f) => {
|
|
728
|
+
if (f.type !== 'unresolved-copy' && f.redefines) {
|
|
729
|
+
const a = f.name.toUpperCase();
|
|
730
|
+
const b = f.redefines.toUpperCase();
|
|
731
|
+
if (known.has(a) && known.has(b) && a !== b) {
|
|
732
|
+
for (const [from, to] of [
|
|
733
|
+
[a, b],
|
|
734
|
+
[b, a],
|
|
735
|
+
]) {
|
|
736
|
+
const k = `${from}->${to}`;
|
|
737
|
+
if (seenRedef.has(k))
|
|
738
|
+
continue;
|
|
739
|
+
seenRedef.add(k);
|
|
740
|
+
assignments.push({
|
|
741
|
+
from,
|
|
742
|
+
to,
|
|
743
|
+
verb: 'REDEFINES',
|
|
744
|
+
line: 0,
|
|
745
|
+
snippet: `${a} REDEFINES ${b}`,
|
|
746
|
+
uncertain: true,
|
|
747
|
+
kind: 'redefines',
|
|
748
|
+
});
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
for (const c of f.children)
|
|
753
|
+
walkRedef(c);
|
|
754
|
+
};
|
|
755
|
+
for (const rec of data.records)
|
|
756
|
+
walkRedef(rec);
|
|
757
|
+
// Dedupe de aristas de sentencia (una misma línea puede repetir el par).
|
|
758
|
+
const edgeSeen = new Set();
|
|
759
|
+
const dedupedEdges = assignments.filter(e => {
|
|
760
|
+
const k = `${e.from}|${e.to}|${e.verb}|${e.line}|${e.kind ?? ''}`;
|
|
761
|
+
if (edgeSeen.has(k))
|
|
762
|
+
return false;
|
|
763
|
+
edgeSeen.add(k);
|
|
764
|
+
return true;
|
|
765
|
+
});
|
|
766
|
+
return { fields, assignments: dedupedEdges, unknownNames: [...unknownNames].sort(), fragment };
|
|
767
|
+
}
|
|
768
|
+
// ── Utilidades y constantes de módulo ────────────────────────────────
|
|
769
|
+
const PROCEDURE_HDR_RE = /^\s*PROCEDURE\s+DIVISION/i;
|
|
770
|
+
const OTHER_DIV_RE = /^\s*(?:IDENTIFICATION|ID|ENVIRONMENT|DATA)\s+DIVISION/i;
|
|
771
|
+
const END_PROGRAM_RE = /^\s*END\s+PROGRAM\b/i;
|
|
772
|
+
const EXEC_START_RE = /(?<![\w-])EXEC\s+(?:SQL|CICS)(?![\w-])/i;
|
|
773
|
+
const END_EXEC_RE = /(?<![\w-])END-EXEC(?![\w-])/i;
|
|
774
|
+
// Un punto cierra la sentencia; el lookahead evita el punto decimal de un
|
|
775
|
+
// literal numérico (MOVE 1.5 TO ...).
|
|
776
|
+
const PERIOD_RE = /\.(?![0-9])/;
|
|
777
|
+
const IDENT_RE = /[A-Za-z][A-Za-z0-9-]*/g;
|
|
778
|
+
const CORRESPONDING_RE = /(?<![\w-])(?:CORRESPONDING|CORR)(?![\w-])/i;
|
|
779
|
+
// Verbos y palabras de control que arrancan (o delimitan) una sentencia:
|
|
780
|
+
// se usan para trocear la sentencia en statements. Incluye verbos que no
|
|
781
|
+
// se modelan (SEARCH, SORT, OPEN…) para que actúen de frontera y su
|
|
782
|
+
// contenido caiga en el handler por defecto (`unclassified`).
|
|
783
|
+
const VERB_SPLIT_RE = /(?<![\w-])(MOVE|COMPUTE|ADD|SUBTRACT|MULTIPLY|DIVIDE|SET|INITIALIZE|ACCEPT|DISPLAY|STRING|UNSTRING|INSPECT|REWRITE|READ|RETURN|WRITE|RELEASE|PERFORM|EVALUATE|WHEN|IF|CALL|GO|SEARCH|SORT|MERGE|UNLOCK|START|DELETE|CANCEL|OPEN|CLOSE)(?![\w-])/gi;
|
|
784
|
+
// Verbos que COPIAN o DERIVAN un valor: de ellos salen aristas de
|
|
785
|
+
// propagación (fuente→destino). READ/RETURN quedan fuera a propósito —
|
|
786
|
+
// su origen es externo (el fichero), no otro campo; el "usos" ya los
|
|
787
|
+
// muestra. IF/EVALUATE/PERFORM tampoco: no mueven datos.
|
|
788
|
+
const EDGE_VERBS = new Set([
|
|
789
|
+
'MOVE',
|
|
790
|
+
'COMPUTE',
|
|
791
|
+
'ADD',
|
|
792
|
+
'SUBTRACT',
|
|
793
|
+
'MULTIPLY',
|
|
794
|
+
'DIVIDE',
|
|
795
|
+
'SET',
|
|
796
|
+
'STRING',
|
|
797
|
+
'UNSTRING',
|
|
798
|
+
'WRITE',
|
|
799
|
+
'REWRITE',
|
|
800
|
+
'RELEASE',
|
|
801
|
+
]);
|
|
802
|
+
/** Índice del primer `kw` (palabra completa, case-insensitive) en `t`, o -1. */
|
|
803
|
+
function kw(t, k) {
|
|
804
|
+
const m = new RegExp(`(?<![\\w-])${k}(?![\\w-])`, 'i').exec(t);
|
|
805
|
+
return m ? m.index : -1;
|
|
806
|
+
}
|
|
807
|
+
/** El menor índice entre varias palabras clave, o -1 si ninguna aparece. */
|
|
808
|
+
function kwAny(t, ks) {
|
|
809
|
+
let best = -1;
|
|
810
|
+
for (const k of ks) {
|
|
811
|
+
const i = kw(t, k);
|
|
812
|
+
if (i >= 0 && (best < 0 || i < best))
|
|
813
|
+
best = i;
|
|
814
|
+
}
|
|
815
|
+
return best;
|
|
816
|
+
}
|
|
817
|
+
/** `text` con el contenido de todo paréntesis reemplazado por espacios
|
|
818
|
+
* (misma longitud): deja fuera subíndices y modificación de referencia. */
|
|
819
|
+
function maskParens(text) {
|
|
820
|
+
let depth = 0;
|
|
821
|
+
let out = '';
|
|
822
|
+
for (const ch of text) {
|
|
823
|
+
if (ch === '(') {
|
|
824
|
+
depth++;
|
|
825
|
+
out += ' ';
|
|
826
|
+
}
|
|
827
|
+
else if (ch === ')') {
|
|
828
|
+
if (depth > 0)
|
|
829
|
+
depth--;
|
|
830
|
+
out += ' ';
|
|
831
|
+
}
|
|
832
|
+
else {
|
|
833
|
+
out += depth > 0 ? ' ' : ch;
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
return out;
|
|
837
|
+
}
|
|
838
|
+
/** Rangos `[inicio, fin)` del contenido de cada paréntesis de primer nivel. */
|
|
839
|
+
function parenRanges(text) {
|
|
840
|
+
const ranges = [];
|
|
841
|
+
let depth = 0;
|
|
842
|
+
let start = -1;
|
|
843
|
+
for (let i = 0; i < text.length; i++) {
|
|
844
|
+
const ch = text[i];
|
|
845
|
+
if (ch === '(') {
|
|
846
|
+
if (depth === 0)
|
|
847
|
+
start = i + 1;
|
|
848
|
+
depth++;
|
|
849
|
+
}
|
|
850
|
+
else if (ch === ')') {
|
|
851
|
+
if (depth > 0)
|
|
852
|
+
depth--;
|
|
853
|
+
if (depth === 0 && start >= 0) {
|
|
854
|
+
ranges.push([start, i]);
|
|
855
|
+
start = -1;
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
return ranges;
|
|
860
|
+
}
|
|
861
|
+
/** Colapsa a una línea y recorta — el snippet de contexto de la ficha. */
|
|
862
|
+
function collapse(text) {
|
|
863
|
+
const one = text.replace(/\s+/g, ' ').trim();
|
|
864
|
+
return one.length > 140 ? one.slice(0, 137) + '…' : one;
|
|
865
|
+
}
|
|
866
|
+
/**
|
|
867
|
+
* Palabras reservadas y estructurales que aparecen en posición de operando
|
|
868
|
+
* y NO son data-names: se excluyen tanto de las referencias como de
|
|
869
|
+
* `unknownNames`. No hace falta que sea exhaustiva — el camino principal
|
|
870
|
+
* se protege casando cada token contra los nombres reales del esquema;
|
|
871
|
+
* esta lista sobre todo evita ruido en `unknownNames` y ubica bien los
|
|
872
|
+
* repartos de rol (dónde corta TO/GIVING/INTO…).
|
|
873
|
+
*/
|
|
874
|
+
const NON_NAME = new Set([
|
|
875
|
+
// Verbos
|
|
876
|
+
'MOVE', 'COMPUTE', 'ADD', 'SUBTRACT', 'MULTIPLY', 'DIVIDE', 'SET', 'INITIALIZE', 'ACCEPT', 'DISPLAY',
|
|
877
|
+
'STRING', 'UNSTRING', 'INSPECT', 'READ', 'RETURN', 'WRITE', 'REWRITE', 'RELEASE', 'PERFORM', 'EVALUATE',
|
|
878
|
+
'WHEN', 'IF', 'ELSE', 'CALL', 'GO', 'GOBACK', 'STOP', 'EXIT', 'CONTINUE', 'SEARCH', 'SORT', 'MERGE',
|
|
879
|
+
'UNLOCK', 'START', 'DELETE', 'CANCEL', 'OPEN', 'CLOSE', 'NEXT', 'SENTENCE', 'RUN',
|
|
880
|
+
// Estructura de sentencia
|
|
881
|
+
'TO', 'FROM', 'BY', 'INTO', 'GIVING', 'USING', 'RETURNING', 'THRU', 'THROUGH', 'ROUNDED', 'REMAINDER',
|
|
882
|
+
'DEPENDING', 'ON', 'VARYING', 'UNTIL', 'TIMES', 'CORRESPONDING', 'CORR', 'OF', 'IN', 'IS', 'ARE', 'THEN',
|
|
883
|
+
'END-IF', 'END-EVALUATE', 'END-PERFORM', 'END-STRING', 'END-UNSTRING', 'END-CALL', 'END-READ', 'END-WRITE',
|
|
884
|
+
'END-REWRITE', 'END-DELETE', 'END-START', 'END-ADD', 'END-SUBTRACT', 'END-MULTIPLY', 'END-DIVIDE',
|
|
885
|
+
'END-COMPUTE', 'END-SEARCH', 'END-RETURN', 'END-ACCEPT', 'END-DISPLAY', 'END-EXEC',
|
|
886
|
+
// Condiciones y relaciones
|
|
887
|
+
'NOT', 'AND', 'OR', 'EQUAL', 'EQUALS', 'GREATER', 'LESS', 'THAN', 'EXCEEDS', 'ZERO', 'ZEROS', 'ZEROES',
|
|
888
|
+
'ZEROED', 'SPACE', 'SPACES', 'HIGH-VALUE', 'HIGH-VALUES', 'LOW-VALUE', 'LOW-VALUES', 'QUOTE', 'QUOTES',
|
|
889
|
+
'NULL', 'NULLS', 'TRUE', 'FALSE', 'OTHER', 'ALSO', 'ANY', 'POSITIVE', 'NEGATIVE', 'NUMERIC', 'ALPHABETIC',
|
|
890
|
+
'ALPHABETIC-LOWER', 'ALPHABETIC-UPPER', 'OMITTED', 'ADDRESS',
|
|
891
|
+
// Cláusulas
|
|
892
|
+
'DELIMITED', 'DELIMITER', 'COUNT', 'POINTER', 'TALLYING', 'REPLACING', 'CONVERTING', 'FOR', 'WITH',
|
|
893
|
+
'MODE', 'KEY', 'LENGTH', 'REFERENCE', 'CONTENT', 'VALUE', 'SIZE', 'ERROR', 'OVERFLOW', 'EXCEPTION',
|
|
894
|
+
'AT', 'INVALID', 'END', 'EOP', 'END-OF-PAGE', 'ADVANCING', 'NO', 'PAGE', 'LINE', 'LINES', 'UP', 'DOWN',
|
|
895
|
+
'INITIAL', 'CHARACTER', 'CHARACTERS', 'ALL', 'LEADING', 'TRAILING', 'FIRST', 'BEFORE', 'AFTER',
|
|
896
|
+
'STANDARD', 'OPTIONAL', 'PRIOR', 'LOCK', 'REWIND', 'REEL', 'UNIT', 'REMOVAL', 'DATA', 'RECORD',
|
|
897
|
+
// SQL / CICS que puedan colarse como token suelto
|
|
898
|
+
'EXEC', 'SQL', 'CICS', 'SELECT', 'INSERT', 'UPDATE', 'FETCH', 'DECLARE', 'CURSOR', 'ORDER', 'GROUP',
|
|
899
|
+
'HAVING', 'WHERE', 'VALUES', 'RESP', 'RESP2', 'INDICATOR', 'CIC',
|
|
900
|
+
// FUNCTION y funciones intrínsecas (FUNCTION MOD (X 4), FUNCTION MAX (...))
|
|
901
|
+
'FUNCTION', 'MOD', 'REM', 'MAX', 'MIN', 'SUM', 'MEAN', 'MEDIAN', 'RANGE', 'MIDRANGE', 'ABS', 'SQRT',
|
|
902
|
+
'NUMVAL', 'NUMVAL-C', 'INTEGER', 'INTEGER-PART', 'INTEGER-OF-DATE', 'DATE-OF-INTEGER', 'RANDOM',
|
|
903
|
+
'CURRENT-DATE', 'WHEN-COMPILED', 'ORD', 'ORD-MAX', 'ORD-MIN', 'CHAR', 'UPPER-CASE', 'LOWER-CASE',
|
|
904
|
+
'REVERSE', 'TRIM', 'LENGTH-AN', 'STORED-CHAR-LENGTH', 'FACTORIAL', 'LOG', 'LOG10', 'EXP', 'EXP10',
|
|
905
|
+
]);
|
|
906
|
+
//# sourceMappingURL=references.js.map
|