@portll/cobolwork 0.6.0 → 0.7.0
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/README.md +49 -18
- package/STABILITY.md +76 -0
- package/bin/cobolwork.mjs +30 -7
- package/lib/bms.mjs +21 -7
- package/lib/build.mjs +108 -53
- package/lib/capabilities.mjs +9 -10
- package/lib/cics-commands.mjs +501 -9
- package/lib/compliance.mjs +11 -0
- package/lib/consequence.mjs +13 -0
- package/lib/control-workers.mjs +161 -0
- package/lib/control.mjs +52 -3
- package/lib/dataflow.mjs +176 -125
- package/lib/db2/cursor.mjs +15 -0
- package/lib/db2/read.mjs +170 -0
- package/lib/db2/rules.mjs +77 -0
- package/lib/db2/stmt/alter.mjs +473 -0
- package/lib/db2/stmt/grant.mjs +125 -0
- package/lib/db2/stmt/index.mjs +141 -0
- package/lib/db2/stmt/misc.mjs +325 -0
- package/lib/db2/stmt/routine.mjs +564 -0
- package/lib/db2/stmt/storage.mjs +146 -0
- package/lib/db2/stmt/table.mjs +540 -0
- package/lib/db2/stmt/view.mjs +146 -0
- package/lib/diff.mjs +17 -5
- package/lib/evidence/cli.mjs +14 -3
- package/lib/evidence/record.mjs +1 -1
- package/lib/evidence/store.mjs +44 -31
- package/lib/exec-reading.mjs +51 -0
- package/lib/execution.mjs +3 -2
- package/lib/explain.mjs +2 -0
- package/lib/exploitability.mjs +11 -2
- package/lib/hlasm/asm/data.mjs +13 -1
- package/lib/hlasm/asm/sections.mjs +3 -1
- package/lib/hlasm/instr.mjs +25 -0
- package/lib/hlasm/macro/authorization.mjs +24 -4
- package/lib/hlasm/macro/datasets.mjs +30 -17
- package/lib/hlasm/macro/io.mjs +68 -45
- package/lib/hlasm/macro/le.mjs +2 -2
- package/lib/hlasm/macro/operator.mjs +32 -21
- package/lib/hlasm/macro/program.mjs +126 -84
- package/lib/hlasm/macro/recovery.mjs +10 -5
- package/lib/hlasm/macro/storage.mjs +64 -14
- package/lib/hlasm/macro/structured.mjs +1 -1
- package/lib/hlasm/model.mjs +35 -10
- package/lib/hlasm/mvs38.mjs +47 -0
- package/lib/hlasm/operands.mjs +10 -0
- package/lib/hlasm/optable.mjs +61 -0
- package/lib/hlasm/read.mjs +22 -8
- package/lib/hlasm.mjs +44 -7
- package/lib/ims/dli.mjs +37 -0
- package/lib/ims/macro/dbd.mjs +299 -0
- package/lib/ims/macro/psb.mjs +286 -0
- package/lib/ims/model.mjs +149 -0
- package/lib/ims/operands.mjs +23 -0
- package/lib/ims/read.mjs +37 -0
- package/lib/ims/rules.mjs +135 -0
- package/lib/ironwork.mjs +17 -13
- package/lib/kernel/pds-archive.mjs +256 -0
- package/lib/kernel/registry.mjs +14 -8
- package/lib/kernel/shared-pass.mjs +34 -9
- package/lib/kernel/source-tree.mjs +104 -36
- package/lib/kernel/version-key.mjs +17 -0
- package/lib/layout.mjs +26 -31
- package/lib/parser.mjs +136 -17
- package/lib/pli/cursor.mjs +15 -0
- package/lib/pli/expr.mjs +101 -0
- package/lib/pli/include.mjs +82 -0
- package/lib/pli/layout.mjs +125 -0
- package/lib/pli/lex.mjs +198 -0
- package/lib/pli/program.mjs +280 -0
- package/lib/pli/rules/based.mjs +95 -0
- package/lib/pli/rules/conditions.mjs +68 -0
- package/lib/pli/rules/entry.mjs +130 -0
- package/lib/pli/rules/index.mjs +24 -0
- package/lib/pli/rules/preprocessor.mjs +55 -0
- package/lib/pli/statements.mjs +130 -0
- package/lib/pli/stmt/alloc.mjs +45 -0
- package/lib/pli/stmt/assignment.mjs +56 -0
- package/lib/pli/stmt/call.mjs +104 -0
- package/lib/pli/stmt/conditions.mjs +94 -0
- package/lib/pli/stmt/control.mjs +219 -0
- package/lib/pli/stmt/declare.mjs +149 -0
- package/lib/pli/stmt/exec.mjs +55 -0
- package/lib/pli/stmt/io.mjs +239 -0
- package/lib/pli/stmt/misc.mjs +4 -0
- package/lib/pli/stmt/preprocessor.mjs +242 -0
- package/lib/pli/stmt/procedure.mjs +258 -0
- package/lib/pli/stmt/stream.mjs +283 -0
- package/lib/pli/storage.mjs +129 -0
- package/lib/precompile-check.mjs +124 -0
- package/lib/precompile-cics.mjs +7 -3
- package/lib/reach.mjs +11 -2
- package/lib/revision.json +1 -1
- package/lib/sarif.mjs +41 -3
- package/lib/scan.mjs +7 -1
- package/lib/sets/abend.mjs +16 -6
- package/lib/sets/cics.mjs +15 -35
- package/lib/sets/compile.mjs +41 -15
- package/lib/sets/crypto.mjs +5 -3
- package/lib/sets/ddl.mjs +36 -0
- package/lib/sets/flow.mjs +18 -1
- package/lib/sets/hidden.mjs +5 -3
- package/lib/sets/hlasm.mjs +72 -12
- package/lib/sets/ims.mjs +139 -0
- package/lib/sets/log.mjs +6 -6
- package/lib/sets/opaque.mjs +27 -7
- package/lib/sets/pli.mjs +40 -0
- package/lib/sets/recon.mjs +5 -3
- package/lib/sets/secrets.mjs +5 -3
- package/lib/sets/semantics.mjs +3 -0
- package/lib/sets/web.mjs +36 -22
- package/lib/site.mjs +10 -0
- package/lib/sources.mjs +80 -20
- package/lib/statement-cursor.mjs +67 -0
- package/lib/verify.mjs +3 -2
- package/lib/version.mjs +6 -0
- package/package.json +3 -2
- package/rules/compliance-cobit2019.json +432 -1
- package/rules/compliance-dora.json +414 -1
- package/rules/compliance-ffiec.json +414 -1
- package/rules/compliance-nist80053.json +466 -1
- package/rules/hlasm-optables.json +8024 -0
- package/schema/cobolwork-baseline.schema.json +36 -0
- package/schema/cobolwork-build-provenance.schema.json +187 -0
- package/schema/cobolwork-build.schema.json +382 -0
- package/schema/cobolwork-capabilities.schema.json +239 -0
- package/schema/cobolwork-diff.schema.json +217 -0
- package/schema/cobolwork-evidence.schema.json +161 -0
- package/schema/cobolwork-execution.schema.json +53 -0
- package/schema/cobolwork-explain.schema.json +360 -0
- package/schema/cobolwork-finding.schema.json +465 -0
- package/schema/cobolwork-flow.schema.json +465 -0
- package/schema/cobolwork-gate.schema.json +211 -0
- package/schema/cobolwork-inventory.schema.json +206 -0
- package/schema/cobolwork-parse.schema.json +105 -0
- package/schema/cobolwork-reach.schema.json +74 -0
- package/schema/cobolwork-report.schema.json +559 -0
- package/schema/cobolwork-witness.schema.json +107 -0
- package/schema/cobolwork.baseline.schema.json +101 -0
- package/schema/cobolwork.site.schema.json +116 -0
|
@@ -1,23 +1,28 @@
|
|
|
1
1
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
2
|
// Parser for z/OS data-set macros: OPEN, CLOSE, and DCB.
|
|
3
|
-
import { HlasmSyntax, NAME, splitOperands } from '../operands.mjs';
|
|
3
|
+
import { HlasmSyntax, NAME, remoteForm, splitOperands } from '../operands.mjs';
|
|
4
4
|
import { parseAddress } from '../expr.mjs';
|
|
5
|
+
import { MVS38_KEYWORDS, mvs38Form } from '../mvs38.mjs';
|
|
5
6
|
|
|
6
7
|
const OPEN_OPTS = new Set(['INPUT', 'OUTPUT', 'UPDAT', 'EXTEND', 'OUTIN', 'OUTINX', 'INOUT', 'RDBACK', 'DISP', 'LEAVE', 'REREAD']);
|
|
7
8
|
const CLOSE_OPTS = new Set(['REREAD', 'LEAVE', 'REWIND', 'FREE', 'DISP']);
|
|
8
|
-
const DCB_KEYWORDS = new Set(['BFALN', 'BFTEK', 'BLKSIZE', 'BUFCB', 'BUFL', 'BUFNO', 'BUFOFF', 'DCBE', 'DDNAME', 'DEVD', 'DSORG', 'EODAD', 'EROPT', 'EXLST', 'LRECL', 'MACRF', 'NCP', 'OPTCD', 'RECFM', 'SYNAD', 'KEYLEN', 'LIMCT', 'RKP', 'DEN', 'TRTCH', 'PRTSP', 'STACK', 'FUNC', 'MODE', 'CODE'
|
|
9
|
+
const DCB_KEYWORDS = new Set(['BFALN', 'BFTEK', 'BLKSIZE', 'BUFCB', 'BUFL', 'BUFNO', 'BUFOFF', 'DCBE', 'DDNAME', 'DEVD', 'DSORG', 'EODAD', 'EROPT', 'EXLST', 'LRECL', 'MACRF', 'NCP', 'OPTCD', 'RECFM', 'SYNAD', 'KEYLEN', 'LIMCT', 'RKP', 'DEN', 'TRTCH', 'PRTSP', 'STACK', 'FUNC', 'MODE', 'CODE',
|
|
10
|
+
// EXCP (z/OS 3.1 DFSMSdfp Advanced Services, Data Control Block (DCB) Fields).
|
|
11
|
+
'REPOS', 'EOEA', 'PCIA', 'SIOA', 'CENDA', 'XENDA', 'IMSK', 'PGFIX', 'IOBAD']);
|
|
9
12
|
|
|
10
13
|
// (dcb,(options),dcb,(options),...): each DCB address followed by its options, written in parentheses,
|
|
11
|
-
// as one bare word or omitted. A single DCB may be written without the outer parentheses.
|
|
12
|
-
|
|
14
|
+
// as one bare word or omitted. A single DCB may be written without the outer parentheses. The list
|
|
15
|
+
// form (MF=L) and the execute form (MF=(E,addr)) may leave a DCB address empty, to be filled in by
|
|
16
|
+
// the other (z/OS 3.1 DFSMS Macro Instructions for Data Sets, OPEN and CLOSE list and execute forms).
|
|
17
|
+
function parseDcbList(raw, validOpts, emptyAllowed = false) {
|
|
13
18
|
const text = raw.trim();
|
|
14
19
|
const items = text.startsWith('(') && text.endsWith(')') ? splitOperands(text.slice(1, -1)) : [text];
|
|
15
20
|
const dcbs = [];
|
|
16
21
|
for (let i = 0; i < items.length; i++) {
|
|
17
22
|
const dcbRaw = items[i].trim();
|
|
18
|
-
if (!dcbRaw) throw new HlasmSyntax('empty dcb address in list');
|
|
19
|
-
let dcb;
|
|
20
|
-
try { dcb = parseAddress(dcbRaw); } catch { throw new HlasmSyntax(`invalid dcb address: ${dcbRaw}`); }
|
|
23
|
+
if (!dcbRaw && !emptyAllowed) throw new HlasmSyntax('empty dcb address in list');
|
|
24
|
+
let dcb = null;
|
|
25
|
+
if (dcbRaw) try { dcb = parseAddress(dcbRaw); } catch { throw new HlasmSyntax(`invalid dcb address: ${dcbRaw}`); }
|
|
21
26
|
const next = (items[i + 1] ?? '').trim();
|
|
22
27
|
const word = next.toUpperCase();
|
|
23
28
|
let options = [];
|
|
@@ -31,10 +36,17 @@ function parseDcbList(raw, validOpts) {
|
|
|
31
36
|
return dcbs;
|
|
32
37
|
}
|
|
33
38
|
|
|
39
|
+
// The DCB list of OPEN or CLOSE and the form MF= gives it; the execute form may omit the list.
|
|
40
|
+
function dcbOperand(macro, ops, validOpts) {
|
|
41
|
+
const mf = ops.keywords.has('MF') ? remoteForm(ops.keywords.get('MF'), 'MF') : null;
|
|
42
|
+
if (ops.positional.length === 0 && mf?.form === 'E') return { dcbs: [], mf };
|
|
43
|
+
if (ops.positional.length !== 1) throw new HlasmSyntax(`${macro} requires exactly one positional operand`);
|
|
44
|
+
return { dcbs: parseDcbList(ops.positional[0], validOpts, Boolean(mf)), mf };
|
|
45
|
+
}
|
|
46
|
+
|
|
34
47
|
export const parsers = {
|
|
35
48
|
OPEN(st, ops) {
|
|
36
|
-
|
|
37
|
-
const dcbs = parseDcbList(ops.positional[0], OPEN_OPTS);
|
|
49
|
+
const { dcbs, mf } = dcbOperand('OPEN', ops, OPEN_OPTS);
|
|
38
50
|
const keywords = {};
|
|
39
51
|
for (const [k, v] of ops.keywords) {
|
|
40
52
|
if (k === 'TYPE') {
|
|
@@ -49,11 +61,10 @@ export const parsers = {
|
|
|
49
61
|
throw new HlasmSyntax(`unknown keyword for OPEN: ${k}`);
|
|
50
62
|
}
|
|
51
63
|
}
|
|
52
|
-
return { kind: 'OPEN', dcbs, keywords };
|
|
64
|
+
return { kind: 'OPEN', dcbs, ...(mf ? { mf: mf.form === 'L' ? 'L' : { form: 'E', list: mf.list } } : {}), keywords };
|
|
53
65
|
},
|
|
54
66
|
CLOSE(st, ops) {
|
|
55
|
-
|
|
56
|
-
const dcbs = parseDcbList(ops.positional[0], CLOSE_OPTS);
|
|
67
|
+
const { dcbs, mf } = dcbOperand('CLOSE', ops, CLOSE_OPTS);
|
|
57
68
|
const keywords = {};
|
|
58
69
|
for (const [k, v] of ops.keywords) {
|
|
59
70
|
if (k === 'TYPE') {
|
|
@@ -68,10 +79,10 @@ export const parsers = {
|
|
|
68
79
|
throw new HlasmSyntax(`unknown keyword for CLOSE: ${k}`);
|
|
69
80
|
}
|
|
70
81
|
}
|
|
71
|
-
return { kind: 'CLOSE', dcbs, keywords };
|
|
82
|
+
return { kind: 'CLOSE', dcbs, ...(mf ? { mf: mf.form === 'L' ? 'L' : { form: 'E', list: mf.list } } : {}), keywords };
|
|
72
83
|
},
|
|
73
|
-
DCB(st, ops) {
|
|
74
|
-
if (ops.positional.
|
|
84
|
+
DCB(st, ops, opts) {
|
|
85
|
+
if (ops.positional.some((p) => p.trim() !== '')) throw new HlasmSyntax('DCB takes no positional operands');
|
|
75
86
|
const keywords = {};
|
|
76
87
|
let ddname = null;
|
|
77
88
|
let dsorg = null;
|
|
@@ -79,8 +90,10 @@ export const parsers = {
|
|
|
79
90
|
let recfm = null;
|
|
80
91
|
let lrecl = null;
|
|
81
92
|
let blksize = null;
|
|
93
|
+
const legacy = {};
|
|
82
94
|
for (const [k, v] of ops.keywords) {
|
|
83
|
-
if (
|
|
95
|
+
if (MVS38_KEYWORDS.DCB.has(k)) mvs38Form(opts, legacy, 'DCB', `${k}=${String(v).trim()}`);
|
|
96
|
+
else if (!DCB_KEYWORDS.has(k)) throw new HlasmSyntax(`unknown keyword for DCB: ${k}`);
|
|
84
97
|
keywords[k] = v;
|
|
85
98
|
if (k === 'DDNAME') ddname = v.toUpperCase();
|
|
86
99
|
else if (k === 'DSORG') dsorg = v;
|
|
@@ -89,6 +102,6 @@ export const parsers = {
|
|
|
89
102
|
else if (k === 'LRECL') lrecl = v;
|
|
90
103
|
else if (k === 'BLKSIZE') blksize = v;
|
|
91
104
|
}
|
|
92
|
-
return { kind: 'DCB', name: st.name, ddname, dsorg, macrf, recfm, lrecl, blksize, keywords };
|
|
105
|
+
return { kind: 'DCB', ...legacy, name: st.name, ddname, dsorg, macrf, recfm, lrecl, blksize, keywords };
|
|
93
106
|
}
|
|
94
107
|
};
|
package/lib/hlasm/macro/io.mjs
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// Parsers for the z/OS record macros GET, PUT, READ, and WRITE in their standard forms.
|
|
3
3
|
import { HlasmSyntax, NAME } from '../operands.mjs';
|
|
4
4
|
import { parseExpression, parseAddress } from '../expr.mjs';
|
|
5
|
+
import { MVS38_BTAM_TYPES, MVS38_VTAM_RPL_KEYWORDS, mvs38Form } from '../mvs38.mjs';
|
|
5
6
|
|
|
6
7
|
const REG = /^\(\d{1,2}\)$/;
|
|
7
8
|
const MF_VAL = /^(L|\((E|M),\s*[A-Z$#@_][A-Z0-9$#@_]*\))$/i;
|
|
@@ -36,7 +37,17 @@ function parseMf(text) {
|
|
|
36
37
|
return t;
|
|
37
38
|
}
|
|
38
39
|
|
|
40
|
+
// VSAM's GET and PUT name only a request parameter list (z/OS 3.1 DFSMS Macro Instructions for
|
|
41
|
+
// Data Sets, GET and PUT for VSAM): RPL=address, register notation from 1 to 12 included.
|
|
42
|
+
function vsamRequest(macro, ops) {
|
|
43
|
+
const { positional, keywords } = ops;
|
|
44
|
+
if (positional.some((p) => p.trim() !== '')) throw new HlasmSyntax(`${macro} RPL= takes no positional operands`);
|
|
45
|
+
for (const k of keywords.keys()) if (k !== 'RPL') throw new HlasmSyntax(`unknown keyword ${k} in ${macro} RPL=`);
|
|
46
|
+
return { kind: macro, rpl: parseAddr(keywords.get('RPL'), 'RPL address') };
|
|
47
|
+
}
|
|
48
|
+
|
|
39
49
|
function parseGet(st, ops) {
|
|
50
|
+
if (ops.keywords.has('RPL')) return vsamRequest('GET', ops);
|
|
40
51
|
const { positional, keywords } = ops;
|
|
41
52
|
if (positional.length > 2) throw new HlasmSyntax('GET takes at most 2 positional operands');
|
|
42
53
|
if (positional.length === 0) throw new HlasmSyntax('GET requires a dcb address');
|
|
@@ -59,6 +70,7 @@ function parseGet(st, ops) {
|
|
|
59
70
|
}
|
|
60
71
|
|
|
61
72
|
function parsePut(st, ops) {
|
|
73
|
+
if (ops.keywords.has('RPL')) return vsamRequest('PUT', ops);
|
|
62
74
|
const { positional, keywords } = ops;
|
|
63
75
|
if (positional.length > 2) throw new HlasmSyntax('PUT takes at most 2 positional operands');
|
|
64
76
|
if (positional.length === 0) throw new HlasmSyntax('PUT requires a dcb address');
|
|
@@ -77,60 +89,71 @@ function parsePut(st, ops) {
|
|
|
77
89
|
return node;
|
|
78
90
|
}
|
|
79
91
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
return node;
|
|
92
|
+
// READ and WRITE name a data event control block, a type, then by position the dcb, area, length,
|
|
93
|
+
// key and block addresses and a next address (z/OS 3.1 DFSMS Macro Instructions for Data Sets: BSAM
|
|
94
|
+
// and BPAM, BDAM, BISAM, and BSAM creating a direct data set). The standard form names the DECB and
|
|
95
|
+
// needs the dcb and area; the list form (MF=L) may leave any operand after the type to the execute
|
|
96
|
+
// form, and the execute form (MF=E) names an existing DECB by address and may omit them too.
|
|
97
|
+
const BLOCK_TYPES = {
|
|
98
|
+
READ: /^(?:SF|SB|SF64P?|K|KU|D[IK][FX]?(?:R|RU)?)$/,
|
|
99
|
+
WRITE: /^(?:SF|SFR|SD|SZ|SF64P?|K|KN|DAF?|D[IK][FX]?)$/,
|
|
100
|
+
};
|
|
101
|
+
const BLOCK_FIELDS = ['dcb', 'area', 'length', 'key', 'block', 'next'];
|
|
102
|
+
const REGISTER = /^\(\s*[A-Z0-9$#@_]+\s*\)$/i;
|
|
103
|
+
|
|
104
|
+
function blockField(field, raw) {
|
|
105
|
+
if (raw === "'S'" || (field === 'key' && raw === '0') || REGISTER.test(raw)) return raw;
|
|
106
|
+
return field === 'length' ? parseLen(raw, 'length') : parseAddr(raw, `${field} address`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// VTAM's basic-mode READ and WRITE: RPL= and the request parameter list fields, no positional operands.
|
|
110
|
+
function vtamBasicRequest(macro, ops, opts) {
|
|
111
|
+
const node = { kind: macro };
|
|
112
|
+
mvs38Form(opts, node, macro, 'RPL=');
|
|
113
|
+
if (ops.positional.some((p) => p.trim() !== '')) throw new HlasmSyntax(`${macro} RPL= takes no positional operands`);
|
|
114
|
+
for (const k of ops.keywords.keys()) if (!MVS38_VTAM_RPL_KEYWORDS.has(k)) throw new HlasmSyntax(`unknown keyword ${k} in ${macro} RPL=`);
|
|
115
|
+
return { ...node, rpl: parseAddr(ops.keywords.get('RPL'), 'RPL address'), keywords: Object.fromEntries(ops.keywords) };
|
|
105
116
|
}
|
|
106
117
|
|
|
107
|
-
function
|
|
118
|
+
function blockRequest(macro, st, ops, opts) {
|
|
108
119
|
const { positional, keywords } = ops;
|
|
109
|
-
if (
|
|
110
|
-
|
|
111
|
-
|
|
120
|
+
if (keywords.has('RPL')) return vtamBasicRequest(macro, ops, opts);
|
|
121
|
+
for (const k of keywords.keys()) if (k !== 'MF') throw new HlasmSyntax(`unknown keyword ${k} in ${macro}`);
|
|
122
|
+
const legacy = {};
|
|
123
|
+
let mf = keywords.has('MF') ? keywords.get('MF').trim().toUpperCase() : null;
|
|
124
|
+
let list = null;
|
|
125
|
+
const remote = mf && /^\(\s*E\s*,(.+)\)$/.exec(mf);
|
|
126
|
+
if (remote) {
|
|
127
|
+
mvs38Form(opts, legacy, macro, 'MF=(E,addr)');
|
|
128
|
+
list = parseAddr(remote[1], 'MF list address');
|
|
129
|
+
mf = 'E';
|
|
130
|
+
}
|
|
131
|
+
if (mf !== null && mf !== 'L' && mf !== 'E') throw new HlasmSyntax(`${macro} MF= value ${mf} is not L or E`);
|
|
132
|
+
if (positional.length < 2) throw new HlasmSyntax(`${macro} requires a decb and a type`);
|
|
133
|
+
if (positional.length > 2 + BLOCK_FIELDS.length) throw new HlasmSyntax(`${macro} takes at most ${2 + BLOCK_FIELDS.length} positional operands`);
|
|
112
134
|
const decb = positional[0].trim();
|
|
113
|
-
if (!
|
|
114
|
-
|
|
135
|
+
if (mf === 'E' ? decb === '' || !(REGISTER.test(decb) || parseAddr(decb, 'decb address')) : !NAME.test(decb)) {
|
|
136
|
+
throw new HlasmSyntax(`${macro} decb name is not a valid symbol`);
|
|
137
|
+
}
|
|
115
138
|
const type = positional[1].trim().toUpperCase();
|
|
116
|
-
if (
|
|
117
|
-
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
if (k === 'MF') {
|
|
127
|
-
node.mf = parseMf(v);
|
|
128
|
-
node.keywords.MF = v;
|
|
139
|
+
if (MVS38_BTAM_TYPES[macro].has(type) && (type !== 'T' || mf === 'E')) mvs38Form(opts, legacy, macro, `type ${type}`);
|
|
140
|
+
else if (!BLOCK_TYPES[macro].test(type)) throw new HlasmSyntax(`${macro} type ${type} is not one the access methods define`);
|
|
141
|
+
const node = { kind: macro, ...legacy, decb, type, mf, ...(list ? { list } : {}), keywords: Object.fromEntries(keywords) };
|
|
142
|
+
BLOCK_FIELDS.forEach((field, i) => {
|
|
143
|
+
const raw = (positional[i + 2] ?? '').trim();
|
|
144
|
+
if (raw === '') {
|
|
145
|
+
if (!mf && (field === 'dcb' || field === 'area')) throw new HlasmSyntax(`${field} address is missing`);
|
|
146
|
+
node[field] = null;
|
|
147
|
+
} else {
|
|
148
|
+
node[field] = blockField(field, raw);
|
|
129
149
|
}
|
|
130
|
-
}
|
|
150
|
+
});
|
|
131
151
|
return node;
|
|
132
152
|
}
|
|
133
153
|
|
|
154
|
+
const parseRead = (st, ops, opts) => blockRequest('READ', st, ops, opts);
|
|
155
|
+
const parseWrite = (st, ops, opts) => blockRequest('WRITE', st, ops, opts);
|
|
156
|
+
|
|
134
157
|
export const parsers = {
|
|
135
158
|
GET: parseGet,
|
|
136
159
|
PUT: parsePut,
|
package/lib/hlasm/macro/le.mjs
CHANGED
|
@@ -17,13 +17,13 @@ function checkKeywords(ops, allowed, op) {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
function parseCEEENTRY(st, ops) {
|
|
20
|
-
if (ops.positional.
|
|
20
|
+
if (ops.positional.some((p) => p.trim() !== '')) throw new HlasmSyntax('CEEENTRY takes no positional operands');
|
|
21
21
|
const keywords = checkKeywords(ops, CEEENTRY_KW, 'CEEENTRY');
|
|
22
22
|
return { kind: 'CEEENTRY', name: st.name, main: keywords.MAIN || null, ppa: keywords.PPA || null, keywords };
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
function parseCEETERM(st, ops) {
|
|
26
|
-
if (ops.positional.
|
|
26
|
+
if (ops.positional.some((p) => p.trim() !== '')) throw new HlasmSyntax('CEETERM takes no positional operands');
|
|
27
27
|
const keywords = checkKeywords(ops, CEETERM_KW, 'CEETERM');
|
|
28
28
|
return { kind: 'CEETERM', rc: keywords.RC || null, keywords };
|
|
29
29
|
}
|
|
@@ -2,8 +2,13 @@
|
|
|
2
2
|
// Parser for the z/OS operator-communication macros WTO and WTOR in their standard form.
|
|
3
3
|
import { HlasmSyntax } from '../operands.mjs';
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
// The standard, list and execute forms, authorized keywords included (z/OS 3.1 MVS Programming:
|
|
6
|
+
// Authorized Assembler Services Reference, Volume 4, WTO and WTOR).
|
|
7
|
+
const WTO_KW = new Set(['TEXT', 'ROUTCDE', 'DESC', 'MCSFLAG', 'MSGTYP', 'CART', 'CONSID', 'CONSNAME', 'KEY', 'TOKEN', 'MF',
|
|
8
|
+
'AREAID', 'CONNECT', 'JOBID', 'JOBNAME', 'LINKAGE', 'SYNCH', 'SYSNAME', 'WSPARM']);
|
|
9
|
+
const WTOR_KW = new Set(['TEXT', 'ROUTCDE', 'MCSFLAG', 'DESC', 'MSGTYP', 'RPLYISUR', 'CART', 'CONSID', 'CONSNAME', 'KEY', 'TOKEN', 'MF',
|
|
10
|
+
'JOBID', 'JOBNAME', 'LINKAGE', 'SYNCH', 'SYSNAME']);
|
|
11
|
+
const isMessage = (text) => text.startsWith("'") || text.startsWith('(');
|
|
7
12
|
|
|
8
13
|
function parseWTO(st, ops) {
|
|
9
14
|
const { positional, keywords } = ops;
|
|
@@ -12,23 +17,24 @@ function parseWTO(st, ops) {
|
|
|
12
17
|
if (!WTO_KW.has(k)) throw new HlasmSyntax(`WTO: unknown keyword ${k}`);
|
|
13
18
|
kw[k] = v;
|
|
14
19
|
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
20
|
+
// A message is one quoted string, or the lines of a multiple-line message, each ('text',type);
|
|
21
|
+
// the execute form (MF=(E,addr)) may leave it to the list form.
|
|
22
|
+
const lines = positional.map((p) => p.trim());
|
|
23
|
+
const execute = /^\(\s*E\s*,/i.test(String(keywords.get('MF') || ''));
|
|
24
|
+
if (lines.length === 1 && lines[0] === '' && execute) return { kind: 'WTO', text: null, keywords: kw };
|
|
25
|
+
for (const line of lines) {
|
|
26
|
+
if (!isMessage(line)) throw new HlasmSyntax('WTO: first positional operand must be a quoted string or a parenthesised list');
|
|
23
27
|
}
|
|
24
|
-
if (
|
|
25
|
-
throw new HlasmSyntax(`WTO: too many positional operands (${
|
|
28
|
+
if (lines.length > 1 && !lines.every((l) => l.startsWith('('))) {
|
|
29
|
+
throw new HlasmSyntax(`WTO: too many positional operands (${lines.length})`);
|
|
26
30
|
}
|
|
27
|
-
return { kind: 'WTO', text, keywords: kw };
|
|
31
|
+
return { kind: 'WTO', text: lines.length ? lines.join(',') : null, keywords: kw };
|
|
28
32
|
}
|
|
29
33
|
|
|
30
34
|
function parseWTOR(st, ops) {
|
|
31
|
-
const {
|
|
35
|
+
const { keywords } = ops;
|
|
36
|
+
const positional = [...ops.positional];
|
|
37
|
+
while (positional.length > 4 && positional.at(-1).trim() === '') positional.pop();
|
|
32
38
|
const kw = {};
|
|
33
39
|
for (const [k, v] of keywords) {
|
|
34
40
|
if (!WTOR_KW.has(k)) throw new HlasmSyntax(`WTOR: unknown keyword ${k}`);
|
|
@@ -39,20 +45,25 @@ function parseWTOR(st, ops) {
|
|
|
39
45
|
let replyLength = null;
|
|
40
46
|
let ecb = null;
|
|
41
47
|
|
|
48
|
+
// The list form (MF=L) may leave the reply, its length and the ECB to the execute form, and the
|
|
49
|
+
// execute form (MF=(E,addr)) any operand to the list form.
|
|
50
|
+
const mf = String(keywords.get('MF') || '').trim();
|
|
51
|
+
const remote = mf !== '';
|
|
52
|
+
const execute = /^\(\s*E\s*,/i.test(mf);
|
|
42
53
|
if (positional.length === 0) {
|
|
43
|
-
if (!keywords.has('TEXT')) {
|
|
54
|
+
if (!keywords.has('TEXT') && !execute) {
|
|
44
55
|
throw new HlasmSyntax('WTOR: no positional operands and no TEXT= keyword');
|
|
45
56
|
}
|
|
46
|
-
} else if (positional.length === 4) {
|
|
57
|
+
} else if (positional.length === 4 || (remote && positional.length <= 4)) {
|
|
47
58
|
const msg = positional[0].trim();
|
|
48
|
-
if (
|
|
59
|
+
if (isMessage(msg)) {
|
|
49
60
|
text = msg;
|
|
50
|
-
} else {
|
|
61
|
+
} else if (!(msg === '' && (execute || keywords.has('TEXT')))) {
|
|
51
62
|
throw new HlasmSyntax('WTOR: first positional operand must be a quoted string or a parenthesised list');
|
|
52
63
|
}
|
|
53
|
-
reply = positional[1];
|
|
54
|
-
replyLength = positional[2];
|
|
55
|
-
ecb = positional[3];
|
|
64
|
+
reply = positional[1] ?? null;
|
|
65
|
+
replyLength = positional[2] ?? null;
|
|
66
|
+
ecb = positional[3] ?? null;
|
|
56
67
|
} else {
|
|
57
68
|
throw new HlasmSyntax(`WTOR: expected 4 positional operands, got ${positional.length}`);
|
|
58
69
|
}
|