Skip to main content

ironwork_rt/lir/
file.rs

1//! Files (lir.md ยง9.4): each file's SELECT and FD, and each file statement with its phrases. The
2//! statements stay `rt::files` calls; these name the places, plans and procedures the walker finds
3//! by name on each execution.
4
5use super::{IntExpr, MovePlan, Operand, PlaceId, RangeId, StorePlan, SymId};
6use crate::files::Format;
7use crate::vocab::{Closing, OpenMode};
8use crate::{codec_enum, codec_struct};
9
10/// A file as SELECT and FD declare it. `format` is how its records are held when its DD does not
11/// say; `status` is FILE STATUS with the MOVE its two characters take.
12#[derive(Clone, Debug, PartialEq, Eq)]
13pub struct FileDesc {
14    pub name: SymId,
15    /// The DD name ASSIGN gives.
16    pub assign: SymId,
17    pub organization: Organization,
18    pub access: Access,
19    pub optional: bool,
20    pub format: Format,
21    /// The shortest and longest variable-length record a READ takes without a record length
22    /// conflict (status 04), as the VLR option measures them (Programming Guide SC27-8714-03,
23    /// pp. 422-424).
24    pub read_lengths: (u32, u32),
25    /// No RECORDING MODE V, and its smallest record as long as its largest.
26    pub fixed: bool,
27    /// The RECORD clause's smallest record.
28    pub record_min: Option<u32>,
29    pub status: Option<(PlaceId, MovePlan)>,
30    /// An indexed file's keys, as spans of its record area.
31    pub keys: Option<IndexKeys>,
32    pub relative: Option<RelativeKey>,
33    pub linage: Option<Linage>,
34    /// A print file's control character.
35    pub carriage: Option<Carriage>,
36    /// Described by SD.
37    pub sort: bool,
38    /// Its own EXCEPTION/ERROR procedure, which comes before one for its open mode.
39    pub error: Option<RangeId>,
40}
41
42#[derive(Clone, Copy, Debug, PartialEq, Eq)]
43pub enum Organization {
44    Sequential,
45    LineSequential,
46    Indexed,
47    Relative,
48}
49
50#[derive(Clone, Copy, Debug, PartialEq, Eq)]
51pub enum Access {
52    Sequential,
53    Random,
54    Dynamic,
55}
56
57/// Bytes of a record: from `offset` in the file's record area, `len` long.
58#[derive(Clone, Copy, Debug, PartialEq, Eq)]
59pub struct RecordSpan {
60    pub offset: u32,
61    pub len: u32,
62}
63
64/// RECORD KEY, then each ALTERNATE RECORD KEY with WITH DUPLICATES; a key of reference numbers
65/// them from 0 in this order.
66#[derive(Clone, Debug, PartialEq, Eq)]
67pub struct IndexKeys {
68    pub prime: RecordSpan,
69    pub alternates: Vec<(RecordSpan, bool)>,
70}
71
72/// The RELATIVE KEY: read as `value`, stored by `store` when a sequential READ or WRITE sets it,
73/// and `digits` the integer digits that bound the record numbers it can hold (None: no bound).
74#[derive(Clone, Debug, PartialEq, Eq)]
75pub struct RelativeKey {
76    pub place: PlaceId,
77    pub value: IntExpr,
78    pub store: StorePlan,
79    pub digits: Option<u32>,
80}
81
82/// LINAGE: each value evaluated, in this order, whenever the page's geometry is taken (at OPEN
83/// OUTPUT or EXTEND and at each new page), and LINAGE-COUNTER with its store.
84#[derive(Clone, Debug, PartialEq, Eq)]
85pub struct Linage {
86    pub lines: IntExpr,
87    pub footing: Option<IntExpr>,
88    pub top: Option<IntExpr>,
89    pub bottom: Option<IntExpr>,
90    pub counter: Option<(PlaceId, StorePlan)>,
91}
92
93/// `machine`: machine codes rather than ASA characters; `reserved`: the character is the record's
94/// own first byte (NOADV) rather than one added before it.
95#[derive(Clone, Copy, Debug, PartialEq, Eq)]
96pub struct Carriage {
97    pub machine: bool,
98    pub reserved: bool,
99}
100
101/// One file statement on one file. `phrase` is AT END or INVALID KEY and its NOT phrase, as
102/// written; `end_of_page` WRITE's END-OF-PAGE and NOT END-OF-PAGE. With neither the op returns
103/// Next; otherwise it returns Arm(1) for the ON phrase, Arm(2) for NOT ON, Arm(3) for END-OF-PAGE,
104/// Arm(4) for NOT END-OF-PAGE, and Arm(0) when no phrase written runs, and a `Select` of
105/// [`FileOp::arms`] blocks follows it.
106#[derive(Clone, Debug, PartialEq, Eq)]
107pub struct FileOp {
108    pub file: u16,
109    pub verb: FileVerb,
110    pub phrase: Option<Phrase>,
111    pub end_of_page: Option<Phrase>,
112}
113
114impl FileOp {
115    /// The blocks the `Select` after the op has: none, 3, or 5 with END-OF-PAGE.
116    pub fn arms(&self) -> usize {
117        match (self.phrase, self.end_of_page) {
118            (None, None) => 0,
119            (_, None) => 3,
120            (_, Some(_)) => 5,
121        }
122    }
123}
124
125#[derive(Clone, Copy, Debug, PartialEq, Eq)]
126pub struct Phrase {
127    pub on: bool,
128    pub not_on: bool,
129}
130
131#[derive(Clone, Debug, PartialEq, Eq)]
132pub enum FileVerb {
133    Open(OpenMode),
134    Close,
135    CloseWith(Closing),
136    /// `sequential`: READ NEXT or PREVIOUS, or a file read in sequence, whose phrase is AT END;
137    /// otherwise the phrase is INVALID KEY and `key` the key of reference of an indexed file.
138    Read { sequential: bool, previous: bool, into: Option<(PlaceId, MovePlan)>, key: u8 },
139    /// `record` is located after FROM has moved into it.
140    Write { record: PlaceId, from: Option<FromMove>, advancing: Option<Advance> },
141    Rewrite { record: PlaceId, from: Option<FromMove> },
142    Delete,
143    Start { rel: StartRel, key: StartKey },
144}
145
146/// WRITE or REWRITE FROM: `to` is the record as a receiving item.
147#[derive(Clone, Copy, Debug, PartialEq, Eq)]
148pub struct FromMove {
149    pub from: Operand,
150    pub to: PlaceId,
151    pub plan: MovePlan,
152}
153
154/// WRITE's ADVANCING phrase. A count below zero moves as zero; PAGE is channel 1, or the next
155/// page of a LINAGE file.
156#[derive(Clone, Debug, PartialEq, Eq)]
157pub enum Advance {
158    Lines { before: bool, count: IntExpr },
159    Page { before: bool },
160    /// A mnemonic-name of SPECIAL-NAMES, as the movement its environment-name gives.
161    Mnemonic { before: bool, space: Spacing },
162}
163
164#[derive(Clone, Copy, Debug, PartialEq, Eq)]
165pub enum Spacing {
166    /// CSP: no spacing.
167    Lines(u64),
168    Channel(u8),
169    /// AFP-5A: page mode data.
170    PageMode,
171}
172
173/// START's relation: KEY =, KEY >, and KEY NOT < or >=.
174#[derive(Clone, Copy, Debug, PartialEq, Eq)]
175pub enum StartRel {
176    Equal,
177    Greater,
178    NotLess,
179}
180
181#[derive(Clone, Debug, PartialEq, Eq)]
182pub enum StartKey {
183    /// An indexed file without KEY: the prime key's bytes in the record area.
184    Prime,
185    /// KEY names key `key`, or a leading part of it: the bytes of `span`.
186    Named { key: u8, span: RecordSpan },
187    /// A relative file's KEY, read as an integer.
188    Relative(IntExpr),
189    /// A relative file without KEY: its RELATIVE KEY.
190    RelativeKey,
191}
192
193codec_struct!(FileDesc {
194    name, assign, organization, access, optional, format, read_lengths, fixed, record_min, status, keys, relative, linage, carriage, sort, error,
195} check file_valid);
196codec_enum!(Organization { Sequential = 0, LineSequential = 1, Indexed = 2, Relative = 3 });
197codec_enum!(Access { Sequential = 0, Random = 1, Dynamic = 2 });
198codec_struct!(RecordSpan { offset, len });
199codec_struct!(IndexKeys { prime, alternates });
200codec_struct!(RelativeKey { place, value, store, digits });
201codec_struct!(Linage { lines, footing, top, bottom, counter });
202codec_struct!(Carriage { machine, reserved });
203codec_struct!(FileOp { file, verb, phrase, end_of_page });
204codec_struct!(Phrase { on, not_on });
205codec_enum!(FileVerb {
206    Open(mode) = 0,
207    Close = 1,
208    Read { sequential, previous, into, key } = 2,
209    Write { record, from, advancing } = 3,
210    Rewrite { record, from } = 4,
211    Delete = 5,
212    Start { rel, key } = 6,
213    CloseWith(closing) = 7,
214});
215codec_struct!(FromMove { from, to, plan });
216codec_enum!(Advance { Lines { before, count } = 0, Page { before } = 1, Mnemonic { before, space } = 2 });
217codec_enum!(Spacing { Lines(n) = 0, Channel(c) = 1, PageMode = 2 });
218codec_enum!(StartRel { Equal = 0, Greater = 1, NotLess = 2 });
219codec_enum!(StartKey { Prime = 0, Named { key, span } = 1, Relative(value) = 2, RelativeKey = 3 });
220
221/// Only an indexed file has keys.
222fn file_valid(file: &FileDesc) -> Result<(), String> {
223    if file.keys.is_some() == (file.organization == Organization::Indexed) {
224        Ok(())
225    } else {
226        Err("keys on a file that is not indexed, or an indexed file without them".into())
227    }
228}