@portll/cobolwork 0.5.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.
Files changed (163) hide show
  1. package/README.md +89 -26
  2. package/STABILITY.md +76 -0
  3. package/bin/cobolwork.mjs +56 -14
  4. package/lib/baseline.mjs +9 -0
  5. package/lib/bms.mjs +21 -7
  6. package/lib/build.mjs +133 -53
  7. package/lib/capabilities.mjs +13 -11
  8. package/lib/cics-commands.mjs +501 -9
  9. package/lib/compliance.mjs +11 -0
  10. package/lib/consequence.mjs +17 -0
  11. package/lib/control-reuse.mjs +113 -0
  12. package/lib/control-workers.mjs +161 -0
  13. package/lib/control.mjs +77 -4
  14. package/lib/dataflow.mjs +402 -152
  15. package/lib/db2/cursor.mjs +15 -0
  16. package/lib/db2/read.mjs +170 -0
  17. package/lib/db2/rules.mjs +77 -0
  18. package/lib/db2/stmt/alter.mjs +473 -0
  19. package/lib/db2/stmt/grant.mjs +125 -0
  20. package/lib/db2/stmt/index.mjs +141 -0
  21. package/lib/db2/stmt/misc.mjs +325 -0
  22. package/lib/db2/stmt/routine.mjs +564 -0
  23. package/lib/db2/stmt/storage.mjs +146 -0
  24. package/lib/db2/stmt/table.mjs +540 -0
  25. package/lib/db2/stmt/view.mjs +146 -0
  26. package/lib/diff.mjs +17 -5
  27. package/lib/equivalence.mjs +44 -5
  28. package/lib/evidence/cli.mjs +26 -7
  29. package/lib/evidence/record.mjs +16 -6
  30. package/lib/evidence/seal.mjs +17 -0
  31. package/lib/evidence/store.mjs +44 -15
  32. package/lib/evidence/timestamp.mjs +83 -0
  33. package/lib/evidence/verify.mjs +120 -42
  34. package/lib/exec-reading.mjs +51 -0
  35. package/lib/execution.mjs +3 -2
  36. package/lib/explain.mjs +2 -0
  37. package/lib/exploitability.mjs +11 -2
  38. package/lib/hlasm/asm/data.mjs +128 -0
  39. package/lib/hlasm/asm/listing.mjs +43 -0
  40. package/lib/hlasm/asm/output.mjs +45 -0
  41. package/lib/hlasm/asm/sections.mjs +161 -0
  42. package/lib/hlasm/asm/symbols.mjs +78 -0
  43. package/lib/hlasm/exec.mjs +27 -0
  44. package/lib/hlasm/expr.mjs +167 -0
  45. package/lib/hlasm/instr.mjs +73 -0
  46. package/lib/hlasm/locate.mjs +333 -0
  47. package/lib/hlasm/macro/authorization.mjs +146 -0
  48. package/lib/hlasm/macro/datasets.mjs +107 -0
  49. package/lib/hlasm/macro/io.mjs +162 -0
  50. package/lib/hlasm/macro/le.mjs +53 -0
  51. package/lib/hlasm/macro/linkage.mjs +143 -0
  52. package/lib/hlasm/macro/operator.mjs +77 -0
  53. package/lib/hlasm/macro/program.mjs +184 -0
  54. package/lib/hlasm/macro/recovery.mjs +85 -0
  55. package/lib/hlasm/macro/storage.mjs +167 -0
  56. package/lib/hlasm/macro/structured.mjs +131 -0
  57. package/lib/hlasm/model.mjs +97 -0
  58. package/lib/hlasm/mvs38.mjs +47 -0
  59. package/lib/hlasm/operands.mjs +59 -0
  60. package/lib/hlasm/optable.mjs +61 -0
  61. package/lib/hlasm/read.mjs +130 -0
  62. package/lib/hlasm.mjs +44 -7
  63. package/lib/ims/dli.mjs +37 -0
  64. package/lib/ims/macro/dbd.mjs +299 -0
  65. package/lib/ims/macro/psb.mjs +286 -0
  66. package/lib/ims/model.mjs +149 -0
  67. package/lib/ims/operands.mjs +23 -0
  68. package/lib/ims/read.mjs +37 -0
  69. package/lib/ims/rules.mjs +135 -0
  70. package/lib/inventory.mjs +10 -7
  71. package/lib/ironwork-ids.mjs +24 -0
  72. package/lib/ironwork.mjs +20 -13
  73. package/lib/kernel/pds-archive.mjs +256 -0
  74. package/lib/kernel/registry.mjs +32 -23
  75. package/lib/kernel/shared-pass.mjs +163 -0
  76. package/lib/kernel/source-tree.mjs +104 -36
  77. package/lib/kernel/version-key.mjs +17 -0
  78. package/lib/layout.mjs +26 -31
  79. package/lib/options.mjs +26 -4
  80. package/lib/parser.mjs +136 -17
  81. package/lib/pli/cursor.mjs +15 -0
  82. package/lib/pli/expr.mjs +101 -0
  83. package/lib/pli/include.mjs +82 -0
  84. package/lib/pli/layout.mjs +125 -0
  85. package/lib/pli/lex.mjs +198 -0
  86. package/lib/pli/program.mjs +280 -0
  87. package/lib/pli/rules/based.mjs +95 -0
  88. package/lib/pli/rules/conditions.mjs +68 -0
  89. package/lib/pli/rules/entry.mjs +130 -0
  90. package/lib/pli/rules/index.mjs +24 -0
  91. package/lib/pli/rules/preprocessor.mjs +55 -0
  92. package/lib/pli/statements.mjs +130 -0
  93. package/lib/pli/stmt/alloc.mjs +45 -0
  94. package/lib/pli/stmt/assignment.mjs +56 -0
  95. package/lib/pli/stmt/call.mjs +104 -0
  96. package/lib/pli/stmt/conditions.mjs +94 -0
  97. package/lib/pli/stmt/control.mjs +219 -0
  98. package/lib/pli/stmt/declare.mjs +149 -0
  99. package/lib/pli/stmt/exec.mjs +55 -0
  100. package/lib/pli/stmt/io.mjs +239 -0
  101. package/lib/pli/stmt/misc.mjs +4 -0
  102. package/lib/pli/stmt/preprocessor.mjs +242 -0
  103. package/lib/pli/stmt/procedure.mjs +258 -0
  104. package/lib/pli/stmt/stream.mjs +283 -0
  105. package/lib/pli/storage.mjs +129 -0
  106. package/lib/policy.mjs +6 -0
  107. package/lib/precompile-check.mjs +124 -0
  108. package/lib/precompile-cics.mjs +8 -4
  109. package/lib/reach.mjs +11 -2
  110. package/lib/revision.json +1 -1
  111. package/lib/sarif.mjs +44 -3
  112. package/lib/scan.mjs +28 -12
  113. package/lib/sets/abend.mjs +82 -8
  114. package/lib/sets/build.mjs +43 -37
  115. package/lib/sets/cics.mjs +20 -38
  116. package/lib/sets/compile.mjs +46 -18
  117. package/lib/sets/copybook.mjs +5 -3
  118. package/lib/sets/crypto.mjs +5 -3
  119. package/lib/sets/ddl.mjs +36 -0
  120. package/lib/sets/flow.mjs +32 -8
  121. package/lib/sets/hidden.mjs +5 -3
  122. package/lib/sets/hlasm.mjs +124 -8
  123. package/lib/sets/ims.mjs +139 -0
  124. package/lib/sets/log.mjs +11 -9
  125. package/lib/sets/opaque.mjs +27 -7
  126. package/lib/sets/pli.mjs +40 -0
  127. package/lib/sets/priv.mjs +5 -3
  128. package/lib/sets/recon.mjs +5 -3
  129. package/lib/sets/secrets.mjs +112 -0
  130. package/lib/sets/semantics.mjs +8 -3
  131. package/lib/sets/web.mjs +39 -23
  132. package/lib/site.mjs +10 -0
  133. package/lib/sources.mjs +80 -20
  134. package/lib/statement-cursor.mjs +67 -0
  135. package/lib/verify.mjs +3 -2
  136. package/lib/version.mjs +6 -0
  137. package/package.json +3 -2
  138. package/rules/compliance-cobit2019.json +520 -5
  139. package/rules/compliance-dora.json +509 -5
  140. package/rules/compliance-ffiec.json +505 -1
  141. package/rules/compliance-nist80053.json +557 -1
  142. package/rules/gitleaks-mainframe.toml +46 -15
  143. package/rules/hlasm-instructions.json +2396 -0
  144. package/rules/hlasm-optables.json +8024 -0
  145. package/schema/cobolwork-baseline.schema.json +36 -0
  146. package/schema/cobolwork-build-provenance.schema.json +187 -0
  147. package/schema/cobolwork-build.schema.json +382 -0
  148. package/schema/cobolwork-capabilities.schema.json +239 -0
  149. package/schema/cobolwork-diff.schema.json +217 -0
  150. package/schema/cobolwork-evidence.schema.json +161 -0
  151. package/schema/cobolwork-execution.schema.json +53 -0
  152. package/schema/cobolwork-explain.schema.json +360 -0
  153. package/schema/cobolwork-finding.schema.json +465 -0
  154. package/schema/cobolwork-flow.schema.json +465 -0
  155. package/schema/cobolwork-gate.schema.json +211 -0
  156. package/schema/cobolwork-inventory.schema.json +206 -0
  157. package/schema/cobolwork-parse.schema.json +105 -0
  158. package/schema/cobolwork-reach.schema.json +74 -0
  159. package/schema/cobolwork-report.schema.json +559 -0
  160. package/schema/cobolwork-witness.schema.json +107 -0
  161. package/schema/cobolwork.baseline.schema.json +101 -0
  162. package/schema/cobolwork.policy.schema.json +7 -0
  163. package/schema/cobolwork.site.schema.json +116 -0
@@ -0,0 +1,219 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // PL/I DO, IF, ELSE, SELECT, WHEN, OTHERWISE, LEAVE, ITERATE, GO TO, RETURN, STOP and EXIT statements.
3
+
4
+ import { cursor } from '../cursor.mjs';
5
+ import { parseExpression, parseReference } from '../expr.mjs';
6
+
7
+
8
+ function parseUnit(c, stmt, ctx, node) {
9
+ const rest = c.drain();
10
+ if (!rest.length) {
11
+ node.units = [];
12
+ return;
13
+ }
14
+ const sub = ctx.subStatement(stmt, rest);
15
+ node.units = [ctx.parseStatement(sub)];
16
+ }
17
+
18
+ function parseDo(c, stmt, ctx) {
19
+ c.expectWord('DO');
20
+ const node = { kind: 'DO', form: 'group', control: null, specs: [], while: null, until: null };
21
+
22
+ if (c.done()) return node;
23
+
24
+ // LOOP, FOREVER, WHILE and UNTIL are words only where a variable of that name could not be:
25
+ // followed by '=', each is the control variable of an iterative DO.
26
+ if (c.isWord('LOOP') && !c.isOp('=', 1)) {
27
+ c.next();
28
+ node.form = 'loop';
29
+ if (!c.done()) c.fail('end of DO LOOP');
30
+ return node;
31
+ }
32
+ if (c.isWord('FOREVER') && !c.isOp('=', 1)) {
33
+ c.next();
34
+ node.form = 'loop';
35
+ if (!c.done()) c.fail('end of DO FOREVER');
36
+ return node;
37
+ }
38
+
39
+ if (c.isWord('WHILE') && !c.isOp('=', 1)) {
40
+ c.next();
41
+ node.form = 'while';
42
+ node.while = parseExpression(c, { stopWords: ['UNTIL'] });
43
+ if (c.isWord('UNTIL') && !c.isOp('=', 1)) {
44
+ c.next();
45
+ node.until = parseExpression(c);
46
+ }
47
+ if (!c.done()) c.fail('end of DO WHILE');
48
+ return node;
49
+ }
50
+
51
+ if (c.isWord('UNTIL') && !c.isOp('=', 1)) {
52
+ c.next();
53
+ node.form = 'while';
54
+ node.until = parseExpression(c, { stopWords: ['WHILE'] });
55
+ if (c.isWord('WHILE')) {
56
+ c.next();
57
+ node.while = parseExpression(c);
58
+ }
59
+ if (!c.done()) c.fail('end of DO UNTIL');
60
+ return node;
61
+ }
62
+
63
+ node.form = 'iterative';
64
+ node.control = parseReference(c);
65
+ c.expectOp('=');
66
+
67
+ while (!c.done()) {
68
+ const spec = { from: null, to: null, by: null, repeat: null, while: null, until: null };
69
+ node.specs.push(spec);
70
+
71
+ spec.from = parseExpression(c);
72
+ if (c.isWord('REPEAT')) {
73
+ c.next();
74
+ spec.repeat = parseExpression(c);
75
+ } else {
76
+ // TO and BY may come in either order, each at most once.
77
+ for (let k = 0; k < 2; k++) {
78
+ if (!spec.to && c.isWord('TO')) { c.next(); spec.to = parseExpression(c); }
79
+ else if (!spec.by && c.isWord('BY')) { c.next(); spec.by = parseExpression(c); }
80
+ }
81
+ }
82
+
83
+ if (c.isWord('WHILE')) {
84
+ c.next();
85
+ spec.while = parseExpression(c, { stopWords: ['UNTIL'] });
86
+ }
87
+ if (c.isWord('UNTIL') && !c.isOp('=', 1)) {
88
+ c.next();
89
+ spec.until = parseExpression(c);
90
+ }
91
+
92
+ if (c.isOp(',')) {
93
+ c.next();
94
+ } else if (!c.done()) {
95
+ c.fail('end of DO iterative');
96
+ }
97
+ }
98
+
99
+ return node;
100
+ }
101
+
102
+ function parseIf(c, stmt, ctx) {
103
+ c.expectWord('IF');
104
+ const node = { kind: 'IF', cond: parseExpression(c, { stopWords: ['THEN'] }), units: [] };
105
+ c.expectWord('THEN');
106
+ parseUnit(c, stmt, ctx, node);
107
+ return node;
108
+ }
109
+
110
+ function parseElse(c, stmt, ctx) {
111
+ c.expectWord('ELSE');
112
+ const node = { kind: 'ELSE', units: [] };
113
+ parseUnit(c, stmt, ctx, node);
114
+ return node;
115
+ }
116
+
117
+ function parseSelect(c, stmt, ctx) {
118
+ c.expectWord('SELECT');
119
+ const node = { kind: 'SELECT', subject: null };
120
+ if (c.isOp('(')) {
121
+ const inner = c.group();
122
+ const s = cursor(inner);
123
+ node.subject = parseExpression(s);
124
+ if (!s.done()) s.fail('end of SELECT subject');
125
+ }
126
+ if (!c.done()) c.fail('end of SELECT');
127
+ return node;
128
+ }
129
+
130
+ function parseWhen(c, stmt, ctx) {
131
+ c.expectWord('WHEN');
132
+ const node = { kind: 'WHEN', values: [], units: [] };
133
+ if (c.isWord(['ANY', 'ALL'])) {
134
+ c.next();
135
+ }
136
+ const items = c.items();
137
+ for (const toks of items) {
138
+ const s = cursor(toks);
139
+ node.values.push(parseExpression(s));
140
+ if (!s.done()) s.fail('end of WHEN value');
141
+ }
142
+ parseUnit(c, stmt, ctx, node);
143
+ return node;
144
+ }
145
+
146
+ function parseOtherwise(c, stmt, ctx) {
147
+ c.word(['OTHERWISE', 'OTHER']);
148
+ const node = { kind: 'OTHERWISE', units: [] };
149
+ parseUnit(c, stmt, ctx, node);
150
+ return node;
151
+ }
152
+
153
+ function parseLeave(c, stmt, ctx) {
154
+ c.expectWord('LEAVE');
155
+ const node = { kind: 'LEAVE', label: null };
156
+ if (!c.done()) {
157
+ node.label = parseReference(c);
158
+ }
159
+ if (!c.done()) c.fail('end of LEAVE');
160
+ return node;
161
+ }
162
+
163
+ function parseIterate(c, stmt, ctx) {
164
+ c.expectWord('ITERATE');
165
+ const node = { kind: 'ITERATE', label: null };
166
+ if (!c.done()) {
167
+ node.label = parseReference(c);
168
+ }
169
+ if (!c.done()) c.fail('end of ITERATE');
170
+ return node;
171
+ }
172
+
173
+ function parseGoto(c, stmt, ctx) {
174
+ c.word(['GO', 'GOTO']);
175
+ if (c.isWord('TO')) c.next();
176
+ const node = { kind: 'GOTO', target: parseReference(c) };
177
+ if (!c.done()) c.fail('end of GO TO');
178
+ return node;
179
+ }
180
+
181
+ function parseReturn(c, stmt, ctx) {
182
+ c.expectWord('RETURN');
183
+ const node = { kind: 'RETURN', value: null };
184
+ if (c.isOp('(')) {
185
+ const inner = c.group();
186
+ const s = cursor(inner);
187
+ node.value = parseExpression(s);
188
+ if (!s.done()) s.fail('end of RETURN value');
189
+ }
190
+ if (!c.done()) c.fail('end of RETURN');
191
+ return node;
192
+ }
193
+
194
+ function parseStop(c, stmt, ctx) {
195
+ c.expectWord('STOP');
196
+ if (!c.done()) c.fail('end of STOP');
197
+ return { kind: 'STOP' };
198
+ }
199
+
200
+ function parseExit(c, stmt, ctx) {
201
+ c.expectWord('EXIT');
202
+ if (!c.done()) c.fail('end of EXIT');
203
+ return { kind: 'EXIT' };
204
+ }
205
+
206
+ export const parsers = {
207
+ DO: parseDo,
208
+ IF: parseIf,
209
+ ELSE: parseElse,
210
+ SELECT: parseSelect,
211
+ WHEN: parseWhen,
212
+ OTHERWISE: parseOtherwise,
213
+ LEAVE: parseLeave,
214
+ ITERATE: parseIterate,
215
+ GOTO: parseGoto,
216
+ RETURN: parseReturn,
217
+ STOP: parseStop,
218
+ EXIT: parseExit,
219
+ };
@@ -0,0 +1,149 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // PL/I DECLARE statements.
3
+ import { cursor, split, PliSyntax } from '../cursor.mjs';
4
+ import { parseExpression, parseReference } from '../expr.mjs';
5
+
6
+ const ABBREV = new Map([
7
+ ['CHAR', 'CHARACTER'], ['BIN', 'BINARY'], ['DEC', 'DECIMAL'], ['PIC', 'PICTURE'],
8
+ ['PTR', 'POINTER'], ['VAR', 'VARYING'], ['VARZ', 'VARYINGZ'], ['NONVAR', 'NONVARYING'],
9
+ ['INIT', 'INITIAL'], ['EXT', 'EXTERNAL'], ['INT', 'INTERNAL'], ['AUTO', 'AUTOMATIC'],
10
+ ['CTL', 'CONTROLLED'], ['DEF', 'DEFINED'], ['POS', 'POSITION'], ['ENV', 'ENVIRONMENT'],
11
+ ['WCHAR', 'WIDECHAR'], ['COND', 'CONDITION'], ['PARM', 'PARAMETER'],
12
+ ]);
13
+
14
+ const KNOWN = new Set([
15
+ 'CHARACTER', 'BIT', 'GRAPHIC', 'WIDECHAR', 'VARYING', 'VARYINGZ', 'NONVARYING',
16
+ 'FIXED', 'FLOAT', 'BINARY', 'DECIMAL', 'PRECISION', 'SIGNED', 'UNSIGNED', 'REAL',
17
+ 'COMPLEX', 'PICTURE', 'POINTER', 'OFFSET', 'HANDLE', 'AREA', 'ENTRY', 'RETURNS',
18
+ 'OPTIONS', 'FILE', 'STREAM', 'RECORD', 'INPUT', 'OUTPUT', 'UPDATE', 'SEQUENTIAL',
19
+ 'DIRECT', 'KEYED', 'ENVIRONMENT', 'PRINT', 'LABEL', 'FORMAT', 'CONDITION', 'BUILTIN',
20
+ 'GENERIC', 'EXTERNAL', 'INTERNAL', 'STATIC', 'AUTOMATIC', 'BASED', 'CONTROLLED',
21
+ 'DEFINED', 'POSITION', 'PARAMETER', 'ALIGNED', 'UNALIGNED', 'INITIAL', 'VALUE',
22
+ 'LIKE', 'UNION', 'CONNECTED', 'NONCONNECTED', 'ASSIGNABLE', 'NONASSIGNABLE',
23
+ 'NORMAL', 'ABNORMAL', 'BYADDR', 'BYVALUE', 'OPTIONAL', 'ORDINAL', 'TYPE', 'DIMACROSS',
24
+ 'BIGENDIAN', 'LITTLEENDIAN', 'HEXADEC', 'IEEE', 'NATIVE', 'NONNATIVE', 'LIMITED',
25
+ 'VARIABLE', 'IRREDUCIBLE', 'REDUCIBLE', 'RESERVED', 'INONLY', 'OUTONLY', 'INOUT',
26
+ 'TASK', 'EVENT', 'BACKWARDS', 'BUFFERED', 'UNBUFFERED', 'TRANSIENT', 'EXCLUSIVE',
27
+ ]);
28
+
29
+ function expandAttr(u) {
30
+ return ABBREV.get(u) || u;
31
+ }
32
+
33
+ function parseAttribute(c, unknown) {
34
+ const w = c.peek();
35
+ if (!w || w.t !== 'word') c.fail('an attribute');
36
+ c.next();
37
+ const name = expandAttr(w.u);
38
+ let args = null;
39
+ let picture;
40
+ if (name === 'PICTURE' && c.peek() && c.peek().t === 'lit') return { name, args, picture: c.next().v };
41
+ if (c.isOp('(')) {
42
+ const inner = c.group();
43
+ if (name === 'PICTURE' && inner.length === 1 && inner[0].t === 'lit') {
44
+ picture = inner[0].v;
45
+ } else {
46
+ args = inner;
47
+ }
48
+ }
49
+ if (!KNOWN.has(name)) unknown.push(name);
50
+ return { name, args, picture };
51
+ }
52
+
53
+ function parseDims(c) {
54
+ const dims = [];
55
+ while (c.isOp('(')) {
56
+ dims.push(...c.items());
57
+ }
58
+ return dims;
59
+ }
60
+
61
+ function parseItem(c, unknown, line) {
62
+ let level = null;
63
+ if (c.peek() && c.peek().t === 'num' && /^\d+$/.test(c.peek().v)) {
64
+ level = parseInt(c.next().v, 10);
65
+ }
66
+
67
+ let name = null;
68
+ let factored = null;
69
+ let dims = [];
70
+
71
+ if (c.isOp('(')) {
72
+ const inner = c.group();
73
+ const parts = split(inner, ',');
74
+ factored = [];
75
+ for (const p of parts) {
76
+ const sc = cursor(p);
77
+ const sub = parseItem(sc, unknown, line);
78
+ if (!sc.done()) sc.fail('end of factored member');
79
+ factored.push(sub);
80
+ }
81
+ } else if (c.isOp('*')) {
82
+ c.next();
83
+ name = '*';
84
+ } else {
85
+ const w = c.peek();
86
+ if (!w || w.t !== 'word') c.fail('a name');
87
+ c.next();
88
+ name = w.u;
89
+ dims = parseDims(c);
90
+ }
91
+
92
+ const attributes = [];
93
+ while (!c.done()) {
94
+ const t = c.peek();
95
+ if (t.t === 'word') {
96
+ attributes.push(parseAttribute(c, unknown));
97
+ } else {
98
+ break;
99
+ }
100
+ }
101
+
102
+ return { level, name, factored, dims, attributes, line };
103
+ }
104
+
105
+ // A factored list gives one item per member, each with its own attributes and the shared ones.
106
+ function flatten(item, names) {
107
+ if (!item.factored) return [{ ...item, factored: names }];
108
+ const all = item.factored.flatMap((m) => (m.factored ? m.factored.map((x) => x.name) : [m.name]));
109
+ return item.factored.flatMap((m) => flatten({ ...m, level: m.level ?? item.level, attributes: [...m.attributes, ...item.attributes] }, all));
110
+ }
111
+
112
+ const isInclude = (p) => p.length === 3 && p[0].t === 'op' && p[0].v === '%' && p[1].t === 'word' && p[1].u === 'INCLUDE' && p[2].t === 'word';
113
+
114
+ // A %INCLUDE where an item belongs supplies the rest of the declaration from a member, which an
115
+ // unexpanded read records by name; a member that is itself the middle of a structure ends at the
116
+ // comma its includer continues from.
117
+ function parseItems(c, unknown, line, { fragment = false } = {}) {
118
+ const items = [];
119
+ const includes = [];
120
+ const parts = split(c.drain(), ',');
121
+ parts.forEach((p, k) => {
122
+ const last = k === parts.length - 1;
123
+ if (isInclude(p) && last) { includes.push(p[2].u); return; }
124
+ if (!p.length && last && fragment && k > 0) return;
125
+ const sc = cursor(p);
126
+ const item = parseItem(sc, unknown, line);
127
+ if (!sc.done()) sc.fail('end of item');
128
+ items.push(...flatten(item, null));
129
+ });
130
+ return { items, includes };
131
+ }
132
+
133
+ function parseDeclare(c, stmt, ctx) {
134
+ c.word(['DECLARE', 'DCL']);
135
+ const unknown = [];
136
+ const { items, includes } = parseItems(c, unknown, stmt.line);
137
+ return { kind: 'DECLARE', items, includes, unknownAttributes: unknown };
138
+ }
139
+
140
+ function parseDeclareFragment(c, stmt, ctx) {
141
+ const unknown = [];
142
+ const { items, includes } = parseItems(c, unknown, stmt.line, { fragment: true });
143
+ return { kind: 'DECLARE_FRAGMENT', items, includes, unknownAttributes: unknown };
144
+ }
145
+
146
+ export const parsers = {
147
+ DECLARE: parseDeclare,
148
+ DECLARE_FRAGMENT: parseDeclareFragment,
149
+ };
@@ -0,0 +1,55 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // PL/I EXEC SQL, EXEC CICS and EXEC DLI statements.
3
+ import { hostVariablesIn } from '../../embedded-sql.mjs';
4
+ import { CICS_COMMANDS, CICS_EVERY_COMMAND } from '../../cics-commands.mjs';
5
+ import { cicsCommand } from '../../precompile-cics.mjs';
6
+
7
+ function toSqlTok(t) {
8
+ if (t.t === 'word') return { t: 'word', u: t.u };
9
+ if (t.t === 'op' && t.v === ':') return { t: 'op', v: ':' };
10
+ if (t.t === 'op' && t.v === '.') return { t: 'period', joined: true };
11
+ if (t.t === 'lit') return { t: 'lit', v: t.v };
12
+ if (t.t === 'num') return { t: 'word', u: t.v };
13
+ return { t: 'word', u: t.v };
14
+ }
15
+
16
+ function parseSql(c) {
17
+ const rest = c.drain();
18
+ const sqlToks = rest.map(toSqlTok);
19
+ const verb = sqlToks.find((t) => t.t === 'word')?.u;
20
+ const hostVars = hostVariablesIn(sqlToks).map((h) => ({ host: h.host, written: h.written }));
21
+ return { verb, hostVars };
22
+ }
23
+
24
+ // The options of an EXEC CICS command as words with their argument tokens, the command resolved by
25
+ // the same table the COBOL translator uses, and each option's direction from it.
26
+ function parseCics(c) {
27
+ const options = [];
28
+ while (!c.done()) {
29
+ const w = c.expectWord();
30
+ options.push({ name: w.u, args: c.isOp('(') ? c.group() : null });
31
+ }
32
+ const name = cicsCommand(options.map((o) => ({ word: o.name })));
33
+ const table = name ? CICS_COMMANDS[name].options : {};
34
+ for (const o of options.slice(1)) o.direction = table[o.name] || CICS_EVERY_COMMAND[o.name] || null;
35
+ return { command: name ? name.split(' ') : options.slice(0, 1).map((o) => o.name), known: !!name, options: options.slice(1) };
36
+ }
37
+
38
+ export const parsers = {
39
+ EXEC(c, stmt, ctx) {
40
+ c.expectWord('EXEC');
41
+ const procTok = c.peek();
42
+ if (!procTok || procTok.t !== 'word') c.fail('a processor word after EXEC');
43
+ const processor = procTok.u;
44
+ c.next();
45
+ const node = { kind: 'EXEC', processor, toks: [] };
46
+ if (processor === 'SQL') {
47
+ node.sql = parseSql(c);
48
+ } else if (processor === 'CICS') {
49
+ node.cics = parseCics(c);
50
+ } else {
51
+ node.toks = c.drain();
52
+ }
53
+ return node;
54
+ },
55
+ };
@@ -0,0 +1,239 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // Parsers for PL/I OPEN, CLOSE, READ, WRITE, REWRITE, DELETE, LOCATE and UNLOCK statements.
3
+
4
+ import { cursor } from '../cursor.mjs';
5
+ import { parseExpression, parseReference } from '../expr.mjs';
6
+
7
+ const OPEN_OPTS = new Set([
8
+ 'STREAM', 'RECORD', 'INPUT', 'OUTPUT', 'UPDATE', 'SEQUENTIAL', 'DIRECT', 'TRANSIENT',
9
+ 'BUFFERED', 'UNBUFFERED', 'KEYED', 'PRINT', 'BACKWARDS', 'EXCLUSIVE',
10
+ 'TITLE', 'LINESIZE', 'PAGESIZE', 'ENVIRONMENT', 'ENV'
11
+ ]);
12
+
13
+ const REF_OPTS = new Set(['INTO', 'FROM', 'SET', 'KEYTO', 'EVENT']);
14
+ const EXPR_OPTS = new Set(['KEY', 'KEYFROM', 'IGNORE', 'TITLE', 'LINESIZE', 'PAGESIZE']);
15
+
16
+ function parseFileRef(c) {
17
+ c.expectWord('FILE');
18
+ c.expectOp('(');
19
+ const ref = parseReference(c);
20
+ c.expectOp(')');
21
+ return ref;
22
+ }
23
+
24
+ function parseOpenFile(c) {
25
+ const file = parseFileRef(c);
26
+ const options = {};
27
+
28
+ while (!c.done()) {
29
+ if (c.isOp(',')) {
30
+ break;
31
+ }
32
+
33
+ if (c.isWord()) {
34
+ const word = c.next();
35
+ const u = word.u;
36
+
37
+ if (OPEN_OPTS.has(u)) {
38
+ if (c.isOp('(')) {
39
+ const inner = c.group();
40
+ const s = cursor(inner);
41
+ if (u === 'ENVIRONMENT' || u === 'ENV') {
42
+ options[u] = inner;
43
+ } else {
44
+ const e = parseExpression(s);
45
+ if (!s.done()) s.fail('the end of the option argument');
46
+ options[u] = e;
47
+ }
48
+ } else {
49
+ options[u] = true;
50
+ }
51
+ } else {
52
+ if (c.isOp('(')) {
53
+ const inner = c.group();
54
+ options[u] = inner;
55
+ } else {
56
+ options[u] = true;
57
+ }
58
+ }
59
+ } else {
60
+ c.fail('an OPEN option');
61
+ }
62
+ }
63
+
64
+ return { file, options };
65
+ }
66
+
67
+ function parseOpen(c, stmt, ctx) {
68
+ c.expectWord('OPEN');
69
+ const files = [];
70
+ files.push(parseOpenFile(c));
71
+
72
+ while (c.isOp(',')) {
73
+ c.next();
74
+ files.push(parseOpenFile(c));
75
+ }
76
+
77
+ return { kind: 'OPEN', files };
78
+ }
79
+
80
+ function parseCloseFile(c) {
81
+ const file = parseFileRef(c);
82
+ const options = {};
83
+
84
+ while (!c.done()) {
85
+ if (c.isOp(',')) {
86
+ break;
87
+ }
88
+
89
+ if (c.isWord('ENVIRONMENT') || c.isWord('ENV')) {
90
+ const u = c.next().u;
91
+ if (c.isOp('(')) {
92
+ const inner = c.group();
93
+ options[u] = inner;
94
+ } else {
95
+ options[u] = true;
96
+ }
97
+ } else {
98
+ c.fail('a CLOSE option');
99
+ }
100
+ }
101
+
102
+ return { file, options };
103
+ }
104
+
105
+ function parseClose(c, stmt, ctx) {
106
+ c.expectWord('CLOSE');
107
+ const files = [];
108
+
109
+ if (c.isWord('FILE') && c.isOp('(', 1)) {
110
+ c.next();
111
+ c.next();
112
+ if (c.isOp('*')) {
113
+ c.next();
114
+ c.expectOp(')');
115
+ files.push({ file: { t: 'star', toks: [] }, options: {} });
116
+ } else {
117
+ const ref = parseReference(c);
118
+ c.expectOp(')');
119
+ files.push({ file: ref, options: {} });
120
+ }
121
+ } else {
122
+ files.push(parseCloseFile(c));
123
+ }
124
+
125
+ while (c.isOp(',')) {
126
+ c.next();
127
+ files.push(parseCloseFile(c));
128
+ }
129
+
130
+ return { kind: 'CLOSE', files };
131
+ }
132
+
133
+ function parseRecordIO(c, stmt, ctx, kind) {
134
+ c.expectWord(kind);
135
+
136
+ const file = parseFileRef(c);
137
+ const options = {};
138
+ let target = null;
139
+
140
+ while (!c.done()) {
141
+ if (c.isWord()) {
142
+ const word = c.next();
143
+ const u = word.u;
144
+
145
+ if (REF_OPTS.has(u)) {
146
+ c.expectOp('(');
147
+ const ref = parseReference(c);
148
+ c.expectOp(')');
149
+ options[u] = ref;
150
+ if (u === 'INTO' || u === 'FROM') target = ref;
151
+ } else if (EXPR_OPTS.has(u)) {
152
+ const inner = c.group();
153
+ const s = cursor(inner);
154
+ const e = parseExpression(s);
155
+ if (!s.done()) s.fail('the end of the option argument');
156
+ options[u] = e;
157
+ } else if (u === 'NOLOCK') {
158
+ options[u] = true;
159
+ } else {
160
+ if (c.isOp('(')) {
161
+ const inner = c.group();
162
+ options[u] = inner;
163
+ } else {
164
+ options[u] = true;
165
+ }
166
+ }
167
+ } else {
168
+ c.fail(`an ${kind} option`);
169
+ }
170
+ }
171
+
172
+ return { kind, file, target, options };
173
+ }
174
+
175
+ function parseLocate(c, stmt, ctx) {
176
+ c.expectWord('LOCATE');
177
+ const target = parseReference(c);
178
+ const file = parseFileRef(c);
179
+ const options = {};
180
+
181
+ while (!c.done()) {
182
+ if (c.isWord()) {
183
+ const word = c.next();
184
+ const u = word.u;
185
+
186
+ if (u === 'SET') {
187
+ c.expectOp('(');
188
+ const ref = parseReference(c);
189
+ c.expectOp(')');
190
+ options[u] = ref;
191
+ } else if (u === 'KEYFROM') {
192
+ const inner = c.group();
193
+ const s = cursor(inner);
194
+ const e = parseExpression(s);
195
+ if (!s.done()) s.fail('the end of the option argument');
196
+ options[u] = e;
197
+ } else {
198
+ if (c.isOp('(')) {
199
+ const inner = c.group();
200
+ options[u] = inner;
201
+ } else {
202
+ options[u] = true;
203
+ }
204
+ }
205
+ } else {
206
+ c.fail('a LOCATE option');
207
+ }
208
+ }
209
+
210
+ return { kind: 'LOCATE', file, target, options };
211
+ }
212
+
213
+ function parseUnlock(c, stmt, ctx) {
214
+ c.expectWord('UNLOCK');
215
+ const file = parseFileRef(c);
216
+ const options = {};
217
+
218
+ if (c.isWord('KEY')) {
219
+ c.next();
220
+ const inner = c.group();
221
+ const s = cursor(inner);
222
+ const e = parseExpression(s);
223
+ if (!s.done()) s.fail('the end of the option argument');
224
+ options['KEY'] = e;
225
+ }
226
+
227
+ return { kind: 'UNLOCK', file, target: null, options };
228
+ }
229
+
230
+ export const parsers = {
231
+ OPEN: parseOpen,
232
+ CLOSE: parseClose,
233
+ READ: (c, s, ctx) => parseRecordIO(c, s, ctx, 'READ'),
234
+ WRITE: (c, s, ctx) => parseRecordIO(c, s, ctx, 'WRITE'),
235
+ REWRITE: (c, s, ctx) => parseRecordIO(c, s, ctx, 'REWRITE'),
236
+ DELETE: (c, s, ctx) => parseRecordIO(c, s, ctx, 'DELETE'),
237
+ LOCATE: parseLocate,
238
+ UNLOCK: parseUnlock
239
+ };
@@ -0,0 +1,4 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // PL/I DEFAULT, DEFINE, WAIT, DELAY, ATTACH, DETACH, ASSERT and QUALIFY statements.
3
+
4
+ export const parsers = {};