Skip to main content

ironwork_rt/cics/
command.rs

1//! An EXEC CICS command as ironwork runs it (lir.md §9.5): which command, and each option it reads
2//! as a place, a value, text or a bare keyword. `P`, `O` and `S` are the executor's handles to a
3//! data item, to any other operand and to text: the LIR's ids by default, the walker's own
4//! references in the interpreter.
5
6use super::Condition;
7use crate::lir::{Operand, ParaId, PlaceId, SymId};
8use crate::module::ModuleError;
9use crate::module::codec::{Decode, Encode, Reader, Writer};
10use crate::{codec_enum, codec_struct};
11
12/// An option's argument: a data item, another operand (a literal, LENGTH OF and the like), text
13/// the translator kept as written (a label, a name that is not a data item), or none at all.
14#[derive(Clone, Copy, Debug, PartialEq, Eq)]
15pub enum Datum<P = PlaceId, O = Operand, S = SymId> {
16    Place(P),
17    Value(O),
18    Text(S),
19    Bare,
20}
21
22/// An option that may be absent.
23pub type Opt<P = PlaceId, O = Operand, S = SymId> = Option<Datum<P, O, S>>;
24
25#[derive(Clone, Debug, PartialEq, Eq)]
26pub struct CicsCommand<P = PlaceId, O = Operand, S = SymId> {
27    /// The command as written, which messages name: SEND for SEND MAP written as SEND MAP(name).
28    pub name: S,
29    pub command: Cics<P, O, S>,
30    pub resp: Resp<P, O, S>,
31}
32
33/// RESP, RESP2 and NOHANDLE, which decide what raising a condition does.
34#[derive(Clone, Debug, PartialEq, Eq)]
35pub struct Resp<P = PlaceId, O = Operand, S = SymId> {
36    pub resp: Opt<P, O, S>,
37    pub resp2: Opt<P, O, S>,
38    pub nohandle: bool,
39}
40
41/// One variant per command ironwork carries out; any other is `Unsupported`.
42#[derive(Clone, Debug, PartialEq, Eq)]
43pub enum Cics<P = PlaceId, O = Operand, S = SymId> {
44    File { verb: FileControl, file: Opt<P, O, S>, options: FileOptions<P, O, S> },
45    Return { transid: Opt<P, O, S>, commarea: Opt<P, O, S>, length: Opt<P, O, S> },
46    Link(Transfer<P, O, S>),
47    Xctl(Transfer<P, O, S>),
48    Abend { abcode: Opt<P, O, S>, cancel: bool },
49    /// Each condition with its paragraph, or None to remove its entry.
50    HandleCondition(Vec<(Condition, Option<ParaId>)>),
51    IgnoreCondition(Vec<Condition>),
52    PushHandle,
53    PopHandle,
54    /// `reset` is CANCEL or RESET; `label` is resolved only when PROGRAM is absent.
55    HandleAbend { program: bool, label: Option<ParaId>, reset: bool },
56    HandleAid,
57    SendMap { map: Opt<P, O, S>, mapset: Opt<P, O, S>, from: Opt<P, O, S>, maponly: bool, dataonly: bool, cursor: Opt<P, O, S>, control: Control },
58    ReceiveMap { map: Opt<P, O, S>, mapset: Opt<P, O, S>, into: Opt<P, O, S>, set: Opt<P, O, S> },
59    SendControl { cursor: Opt<P, O, S>, control: Control },
60    Receive(Record<P, O, S>),
61    Asktime { abstime: Opt<P, O, S> },
62    /// `outputs` are the options that name a date or time form, in the order written.
63    Formattime { abstime: Opt<P, O, S>, datesep: Opt<P, O, S>, timesep: Opt<P, O, S>, outputs: Vec<(S, Datum<P, O, S>)> },
64    Assign(Assign<P, O, S>),
65    Getmain { flength: Opt<P, O, S>, length: Opt<P, O, S>, initimg: Opt<P, O, S>, set: Opt<P, O, S> },
66    Freemain,
67    Enq,
68    Deq,
69    Delay,
70    /// Settles the SQL session's unit of work.
71    Syncpoint { rollback: bool },
72    Address { eib: Opt<P, O, S>, commarea: Opt<P, O, S>, cwa: Opt<P, O, S>, twa: Opt<P, O, S> },
73    SendText { from: Opt<P, O, S>, length: Opt<P, O, S> },
74    WriteOperator { text: Opt<P, O, S>, textlength: Opt<P, O, S> },
75    WriteqTs { queue: Opt<P, O, S>, from: Opt<P, O, S>, length: Opt<P, O, S>, rewrite: bool, item: Opt<P, O, S>, numitems: Opt<P, O, S> },
76    ReadqTs { queue: Opt<P, O, S>, next: bool, item: Opt<P, O, S>, numitems: Opt<P, O, S>, record: Record<P, O, S> },
77    DeleteqTs { queue: Opt<P, O, S> },
78    WriteqTd { queue: Opt<P, O, S>, from: Opt<P, O, S>, length: Opt<P, O, S> },
79    ReadqTd { queue: Opt<P, O, S>, record: Record<P, O, S> },
80    DeleteqTd { queue: Opt<P, O, S> },
81    /// "EXEC CICS … is not supported yet", naming the command, when reached.
82    Unsupported,
83}
84
85#[derive(Clone, Copy, Debug, PartialEq, Eq)]
86pub enum FileControl {
87    Read,
88    Write,
89    Rewrite,
90    Delete,
91    Unlock,
92    Startbr,
93    Resetbr,
94    Readnext,
95    Readprev,
96    Endbr,
97}
98
99#[derive(Clone, Debug, PartialEq, Eq)]
100pub struct FileOptions<P = PlaceId, O = Operand, S = SymId> {
101    pub ridfld: Opt<P, O, S>,
102    pub keylength: Opt<P, O, S>,
103    pub reqid: Opt<P, O, S>,
104    pub from: Opt<P, O, S>,
105    pub numrec: Opt<P, O, S>,
106    pub record: Record<P, O, S>,
107    pub generic: bool,
108    pub rrn: bool,
109    pub gteq: bool,
110    pub equal: bool,
111    pub update: bool,
112}
113
114/// Where a record read arrives: INTO, or SET's pointer; LENGTH, which also limits it. WRITE's
115/// LENGTH is the same option.
116#[derive(Clone, Debug, PartialEq, Eq)]
117pub struct Record<P = PlaceId, O = Operand, S = SymId> {
118    pub into: Opt<P, O, S>,
119    pub set: Opt<P, O, S>,
120    pub length: Opt<P, O, S>,
121}
122
123/// LINK's and XCTL's options.
124#[derive(Clone, Debug, PartialEq, Eq)]
125pub struct Transfer<P = PlaceId, O = Operand, S = SymId> {
126    pub program: Opt<P, O, S>,
127    pub commarea: Opt<P, O, S>,
128    pub length: Opt<P, O, S>,
129}
130
131/// SEND MAP's and SEND CONTROL's write command and write control character options.
132#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
133pub struct Control {
134    pub erase: bool,
135    pub freekb: bool,
136    pub alarm: bool,
137    pub frset: bool,
138}
139
140/// The ASSIGN options ironwork answers.
141#[derive(Clone, Debug, PartialEq, Eq)]
142pub struct Assign<P = PlaceId, O = Operand, S = SymId> {
143    pub applid: Opt<P, O, S>,
144    pub sysid: Opt<P, O, S>,
145    pub userid: Opt<P, O, S>,
146    pub netname: Opt<P, O, S>,
147    pub facility: Opt<P, O, S>,
148    pub startcode: Opt<P, O, S>,
149    pub abcode: Opt<P, O, S>,
150    pub program: Opt<P, O, S>,
151    pub cwaleng: Opt<P, O, S>,
152    pub twaleng: Opt<P, O, S>,
153}
154
155impl<P, O, S> Cics<P, O, S> {
156    /// The variant's name, as the command it carries out is spelt.
157    pub fn name(&self) -> &'static str {
158        match self {
159            Self::File { verb, .. } => verb.name(),
160            Self::Return { .. } => "RETURN",
161            Self::Link(_) => "LINK",
162            Self::Xctl(_) => "XCTL",
163            Self::Abend { .. } => "ABEND",
164            Self::HandleCondition(_) => "HANDLE CONDITION",
165            Self::IgnoreCondition(_) => "IGNORE CONDITION",
166            Self::PushHandle => "PUSH HANDLE",
167            Self::PopHandle => "POP HANDLE",
168            Self::HandleAbend { .. } => "HANDLE ABEND",
169            Self::HandleAid => "HANDLE AID",
170            Self::SendMap { .. } => "SEND MAP",
171            Self::ReceiveMap { .. } => "RECEIVE MAP",
172            Self::SendControl { .. } => "SEND CONTROL",
173            Self::Receive(_) => "RECEIVE",
174            Self::Asktime { .. } => "ASKTIME",
175            Self::Formattime { .. } => "FORMATTIME",
176            Self::Assign(_) => "ASSIGN",
177            Self::Getmain { .. } => "GETMAIN",
178            Self::Freemain => "FREEMAIN",
179            Self::Enq => "ENQ",
180            Self::Deq => "DEQ",
181            Self::Delay => "DELAY",
182            Self::Syncpoint { .. } => "SYNCPOINT",
183            Self::Address { .. } => "ADDRESS",
184            Self::SendText { .. } => "SEND TEXT",
185            Self::WriteOperator { .. } => "WRITE OPERATOR",
186            Self::WriteqTs { .. } => "WRITEQ TS",
187            Self::ReadqTs { .. } => "READQ TS",
188            Self::DeleteqTs { .. } => "DELETEQ TS",
189            Self::WriteqTd { .. } => "WRITEQ TD",
190            Self::ReadqTd { .. } => "READQ TD",
191            Self::DeleteqTd { .. } => "DELETEQ TD",
192            Self::Unsupported => "unsupported",
193        }
194    }
195}
196
197/// Turns a command's handles into another executor's, as lowering turns the walker's references
198/// into the LIR's ids.
199pub trait Handles<P, O, S> {
200    type Place;
201    type Value;
202    type Text;
203    type Error;
204    fn place(&mut self, place: P) -> Result<Self::Place, Self::Error>;
205    fn value(&mut self, value: O) -> Result<Self::Value, Self::Error>;
206    fn text(&mut self, text: S) -> Result<Self::Text, Self::Error>;
207}
208
209type Mapped<H, P, O, S, T> = Result<T, <H as Handles<P, O, S>>::Error>;
210type DatumOf<H, P, O, S> = Datum<<H as Handles<P, O, S>>::Place, <H as Handles<P, O, S>>::Value, <H as Handles<P, O, S>>::Text>;
211type OptOf<H, P, O, S> = Option<DatumOf<H, P, O, S>>;
212type CommandOf<H, P, O, S> = CicsCommand<<H as Handles<P, O, S>>::Place, <H as Handles<P, O, S>>::Value, <H as Handles<P, O, S>>::Text>;
213type CicsOf<H, P, O, S> = Cics<<H as Handles<P, O, S>>::Place, <H as Handles<P, O, S>>::Value, <H as Handles<P, O, S>>::Text>;
214type RecordOf<H, P, O, S> = Record<<H as Handles<P, O, S>>::Place, <H as Handles<P, O, S>>::Value, <H as Handles<P, O, S>>::Text>;
215type TransferOf<H, P, O, S> = Transfer<<H as Handles<P, O, S>>::Place, <H as Handles<P, O, S>>::Value, <H as Handles<P, O, S>>::Text>;
216
217impl<P, O, S> Datum<P, O, S> {
218    pub fn map<H: Handles<P, O, S>>(self, h: &mut H) -> Mapped<H, P, O, S, DatumOf<H, P, O, S>> {
219        Ok(match self {
220            Self::Place(p) => Datum::Place(h.place(p)?),
221            Self::Value(o) => Datum::Value(h.value(o)?),
222            Self::Text(s) => Datum::Text(h.text(s)?),
223            Self::Bare => Datum::Bare,
224        })
225    }
226}
227
228fn opt<P, O, S, H: Handles<P, O, S>>(o: Opt<P, O, S>, h: &mut H) -> Mapped<H, P, O, S, OptOf<H, P, O, S>> {
229    o.map(|d| d.map(h)).transpose()
230}
231
232impl<P, O, S> CicsCommand<P, O, S> {
233    /// The same command over `h`'s handles, each mapped in the order the fields are written.
234    pub fn map<H: Handles<P, O, S>>(self, h: &mut H) -> Mapped<H, P, O, S, CommandOf<H, P, O, S>> {
235        let name = h.text(self.name)?;
236        let command = self.command.map(h)?;
237        let resp = Resp { resp: opt(self.resp.resp, h)?, resp2: opt(self.resp.resp2, h)?, nohandle: self.resp.nohandle };
238        Ok(CicsCommand { name, command, resp })
239    }
240}
241
242impl<P, O, S> Record<P, O, S> {
243    fn map<H: Handles<P, O, S>>(self, h: &mut H) -> Mapped<H, P, O, S, RecordOf<H, P, O, S>> {
244        Ok(Record { into: opt(self.into, h)?, set: opt(self.set, h)?, length: opt(self.length, h)? })
245    }
246}
247
248impl<P, O, S> Transfer<P, O, S> {
249    fn map<H: Handles<P, O, S>>(self, h: &mut H) -> Mapped<H, P, O, S, TransferOf<H, P, O, S>> {
250        Ok(Transfer { program: opt(self.program, h)?, commarea: opt(self.commarea, h)?, length: opt(self.length, h)? })
251    }
252}
253
254impl<P, O, S> Cics<P, O, S> {
255    fn map<H: Handles<P, O, S>>(self, h: &mut H) -> Mapped<H, P, O, S, CicsOf<H, P, O, S>> {
256        Ok(match self {
257            Self::File { verb, file, options: o } => Cics::File {
258                verb,
259                file: opt(file, h)?,
260                options: FileOptions {
261                    ridfld: opt(o.ridfld, h)?,
262                    keylength: opt(o.keylength, h)?,
263                    reqid: opt(o.reqid, h)?,
264                    from: opt(o.from, h)?,
265                    numrec: opt(o.numrec, h)?,
266                    record: o.record.map(h)?,
267                    generic: o.generic,
268                    rrn: o.rrn,
269                    gteq: o.gteq,
270                    equal: o.equal,
271                    update: o.update,
272                },
273            },
274            Self::Return { transid, commarea, length } => Cics::Return { transid: opt(transid, h)?, commarea: opt(commarea, h)?, length: opt(length, h)? },
275            Self::Link(t) => Cics::Link(t.map(h)?),
276            Self::Xctl(t) => Cics::Xctl(t.map(h)?),
277            Self::Abend { abcode, cancel } => Cics::Abend { abcode: opt(abcode, h)?, cancel },
278            Self::HandleCondition(labels) => Cics::HandleCondition(labels),
279            Self::IgnoreCondition(conditions) => Cics::IgnoreCondition(conditions),
280            Self::PushHandle => Cics::PushHandle,
281            Self::PopHandle => Cics::PopHandle,
282            Self::HandleAbend { program, label, reset } => Cics::HandleAbend { program, label, reset },
283            Self::HandleAid => Cics::HandleAid,
284            Self::SendMap { map, mapset, from, maponly, dataonly, cursor, control } => {
285                Cics::SendMap { map: opt(map, h)?, mapset: opt(mapset, h)?, from: opt(from, h)?, maponly, dataonly, cursor: opt(cursor, h)?, control }
286            }
287            Self::ReceiveMap { map, mapset, into, set } => Cics::ReceiveMap { map: opt(map, h)?, mapset: opt(mapset, h)?, into: opt(into, h)?, set: opt(set, h)? },
288            Self::SendControl { cursor, control } => Cics::SendControl { cursor: opt(cursor, h)?, control },
289            Self::Receive(record) => Cics::Receive(record.map(h)?),
290            Self::Asktime { abstime } => Cics::Asktime { abstime: opt(abstime, h)? },
291            Self::Formattime { abstime, datesep, timesep, outputs } => {
292                let (abstime, datesep, timesep) = (opt(abstime, h)?, opt(datesep, h)?, opt(timesep, h)?);
293                let outputs = outputs.into_iter().map(|(name, d)| Ok((h.text(name)?, d.map(h)?))).collect::<Result<_, _>>()?;
294                Cics::Formattime { abstime, datesep, timesep, outputs }
295            }
296            Self::Assign(a) => Cics::Assign(Assign {
297                applid: opt(a.applid, h)?,
298                sysid: opt(a.sysid, h)?,
299                userid: opt(a.userid, h)?,
300                netname: opt(a.netname, h)?,
301                facility: opt(a.facility, h)?,
302                startcode: opt(a.startcode, h)?,
303                abcode: opt(a.abcode, h)?,
304                program: opt(a.program, h)?,
305                cwaleng: opt(a.cwaleng, h)?,
306                twaleng: opt(a.twaleng, h)?,
307            }),
308            Self::Getmain { flength, length, initimg, set } => Cics::Getmain { flength: opt(flength, h)?, length: opt(length, h)?, initimg: opt(initimg, h)?, set: opt(set, h)? },
309            Self::Freemain => Cics::Freemain,
310            Self::Enq => Cics::Enq,
311            Self::Deq => Cics::Deq,
312            Self::Delay => Cics::Delay,
313            Self::Syncpoint { rollback } => Cics::Syncpoint { rollback },
314            Self::Address { eib, commarea, cwa, twa } => Cics::Address { eib: opt(eib, h)?, commarea: opt(commarea, h)?, cwa: opt(cwa, h)?, twa: opt(twa, h)? },
315            Self::SendText { from, length } => Cics::SendText { from: opt(from, h)?, length: opt(length, h)? },
316            Self::WriteOperator { text, textlength } => Cics::WriteOperator { text: opt(text, h)?, textlength: opt(textlength, h)? },
317            Self::WriteqTs { queue, from, length, rewrite, item, numitems } => {
318                Cics::WriteqTs { queue: opt(queue, h)?, from: opt(from, h)?, length: opt(length, h)?, rewrite, item: opt(item, h)?, numitems: opt(numitems, h)? }
319            }
320            Self::ReadqTs { queue, next, item, numitems, record } => {
321                Cics::ReadqTs { queue: opt(queue, h)?, next, item: opt(item, h)?, numitems: opt(numitems, h)?, record: record.map(h)? }
322            }
323            Self::DeleteqTs { queue } => Cics::DeleteqTs { queue: opt(queue, h)? },
324            Self::WriteqTd { queue, from, length } => Cics::WriteqTd { queue: opt(queue, h)?, from: opt(from, h)?, length: opt(length, h)? },
325            Self::ReadqTd { queue, record } => Cics::ReadqTd { queue: opt(queue, h)?, record: record.map(h)? },
326            Self::DeleteqTd { queue } => Cics::DeleteqTd { queue: opt(queue, h)? },
327            Self::Unsupported => Cics::Unsupported,
328        })
329    }
330
331    /// The paragraphs HANDLE CONDITION and HANDLE ABEND name.
332    pub fn labels(&self) -> Vec<ParaId> {
333        match self {
334            Self::HandleCondition(labels) => labels.iter().filter_map(|&(_, p)| p).collect(),
335            Self::HandleAbend { label, .. } => label.iter().copied().collect(),
336            _ => Vec::new(),
337        }
338    }
339}
340
341impl FileControl {
342    pub fn name(self) -> &'static str {
343        match self {
344            Self::Read => "READ",
345            Self::Write => "WRITE",
346            Self::Rewrite => "REWRITE",
347            Self::Delete => "DELETE",
348            Self::Unlock => "UNLOCK",
349            Self::Startbr => "STARTBR",
350            Self::Resetbr => "RESETBR",
351            Self::Readnext => "READNEXT",
352            Self::Readprev => "READPREV",
353            Self::Endbr => "ENDBR",
354        }
355    }
356}
357
358/// A condition's tag is its place in DFHRESP's order.
359impl Encode for Condition {
360    fn encode(&self, w: &mut Writer) {
361        w.leb(*self as u64);
362    }
363}
364
365impl Decode for Condition {
366    fn decode(r: &mut Reader<'_>) -> Result<Self, ModuleError> {
367        let at = r.position();
368        let tag = r.leb()?;
369        usize::try_from(tag).ok().and_then(|i| Condition::ALL.get(i).copied()).ok_or_else(|| r.malformed(at, format!("Condition has no tag {tag}")))
370    }
371}
372
373codec_enum!(Datum { Place(place) = 0, Value(value) = 1, Text(text) = 2, Bare = 3 });
374codec_struct!(CicsCommand { name, command, resp });
375codec_struct!(Resp { resp, resp2, nohandle });
376codec_enum!(Cics {
377    File { verb, file, options } = 0,
378    Return { transid, commarea, length } = 1,
379    Link(transfer) = 2,
380    Xctl(transfer) = 3,
381    Abend { abcode, cancel } = 4,
382    HandleCondition(handlers) = 5,
383    IgnoreCondition(conditions) = 6,
384    PushHandle = 7,
385    PopHandle = 8,
386    HandleAbend { program, label, reset } = 9,
387    HandleAid = 10,
388    SendMap { map, mapset, from, maponly, dataonly, cursor, control } = 11,
389    ReceiveMap { map, mapset, into, set } = 12,
390    SendControl { cursor, control } = 13,
391    Receive(record) = 14,
392    Asktime { abstime } = 15,
393    Formattime { abstime, datesep, timesep, outputs } = 16,
394    Assign(assign) = 17,
395    Getmain { flength, length, initimg, set } = 18,
396    Freemain = 19,
397    Enq = 20,
398    Deq = 21,
399    Delay = 22,
400    Syncpoint { rollback } = 23,
401    Address { eib, commarea, cwa, twa } = 24,
402    SendText { from, length } = 25,
403    WriteOperator { text, textlength } = 26,
404    WriteqTs { queue, from, length, rewrite, item, numitems } = 27,
405    ReadqTs { queue, next, item, numitems, record } = 28,
406    DeleteqTs { queue } = 29,
407    WriteqTd { queue, from, length } = 30,
408    ReadqTd { queue, record } = 31,
409    DeleteqTd { queue } = 32,
410    Unsupported = 33,
411});
412codec_enum!(FileControl {
413    Read = 0,
414    Write = 1,
415    Rewrite = 2,
416    Delete = 3,
417    Unlock = 4,
418    Startbr = 5,
419    Resetbr = 6,
420    Readnext = 7,
421    Readprev = 8,
422    Endbr = 9,
423});
424codec_struct!(FileOptions { ridfld, keylength, reqid, from, numrec, record, generic, rrn, gteq, equal, update });
425codec_struct!(Record { into, set, length });
426codec_struct!(Transfer { program, commarea, length });
427codec_struct!(Control { erase, freekb, alarm, frset });
428codec_struct!(Assign { applid, sysid, userid, netname, facility, startcode, abcode, program, cwaleng, twaleng });