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