Skip to main content

ironwork_rt/lir/
mod.rs

1//! The LIR of docs/lir.md: a program lowered once, which the VM runs and a load module holds.
2
3mod arith;
4mod call;
5mod class;
6mod codec;
7mod collating;
8mod debug;
9mod file;
10mod flow;
11mod markup;
12mod payload;
13mod place;
14mod print;
15mod scope;
16mod sort;
17mod sql;
18mod text;
19mod value;
20
21pub use arith::{ArithPlan, ArithStep, Mode, RemainderPlan, StepPlan, StorePlan, UpDown};
22pub use call::{CallArg, CallPlan, CallTarget, EntryPoint, FunctionDefinition, LeService, UserArgument, UserFunctionPlan};
23pub use class::{Class, ClassPart, Method};
24pub use collating::{Collating, Sequence};
25pub use debug::Debug;
26pub use file::{
27    Access, Advance, AssignItem, Carriage, FileDesc, FileOp, FileVerb, FromMove, IndexKeys, Linage, Organization, Phrase, RecordDepending,
28    RecordSpan, RelativeKey, Spacing, StartKey, StartRel,
29};
30pub use markup::{
31    Ccsid, Convert, Flag, Indicator, JsonGenerate, JsonLeaf, JsonNode, JsonParse, JsonValue, Marker, Markup, Named, NumberInto, ParseLeaf, ParseNode, ParseValue,
32    SetTo, XmlForm, XmlGenerate, XmlNode, XmlParse, XmlRegister, XmlValue,
33};
34pub use flow::{Declaratives, Frame, FrameKind, Op, Range, RangeKind, Resume, ReturnPoint, Returns, Step, Terminator};
35pub use crate::cics::CicsCommand;
36pub use crate::report::{ReportOp, Writer as ReportWriter};
37pub use payload::{
38    Argument, DisplayItem, DisplayPlan, FloatFrom, Func, FunctionPlan, Image, InitField, InitPlan, InitValue,
39    InvokePlan, MethodName, MovePlan, NationalFrom, NumericFrom, PlaceNumcheck, Receiver, SearchAllPlan, SearchKey, SenderCheck, TrimSide,
40};
41pub use place::{Base, Odo, Place, RefMod, Subscript};
42pub use print::{Code, Listing};
43pub use scope::{Binding, Global, GlobalAt, Scope, Section, SharedFile};
44pub use sort::{FileSort, ReleasePlan, ReturnPlan, SortIo, SortKey, SortKeys, SortPlan, TableSort};
45pub use sql::{HostPlace, SqlEntry, SqlStatement, Sqlca, SqlcaField};
46pub use text::{
47    Bound, Chars, ConvertTable, Converting, DelimiterIn, InspectPhrase, InspectPlan, Inspected, Replacement,
48    StringPlan, StringSource, UnstringInto, UnstringPlan,
49};
50pub use value::{ByteClass, Compare, Comparand, Cond, Const, Count, Expr, IntExpr, Operand, SignTest, SqlTest};
51
52use crate::abend::AbendCode;
53use crate::{codec_enum, codec_struct};
54use crate::picture::Sym;
55use crate::storage::Kind;
56
57pub type BlockId = u32;
58pub type ParaId = u32;
59pub type RangeId = u32;
60pub type PlaceId = u32;
61pub type ExprId = u32;
62pub type CondId = u32;
63pub type ConstId = u32;
64pub type SymId = u32;
65pub type DebugId = u32;
66pub type AbendId = u32;
67/// A PERFORM TIMES counter, held in the frame the statement runs under (`Frame.temps`).
68pub type TempId = u16;
69pub type SqlId = u32;
70pub type ArithId = u32;
71pub type InitId = u32;
72pub type DisplayId = u32;
73pub type InspectId = u32;
74pub type StringId = u32;
75pub type UnstringId = u32;
76pub type SearchAllId = u32;
77pub type FunctionId = u32;
78pub type UserFunctionId = u32;
79pub type FileOpId = u32;
80pub type CallId = u32;
81pub type SortId = u32;
82pub type ReleaseId = u32;
83pub type ReturnId = u32;
84pub type InvokeId = u32;
85pub type CicsId = u32;
86pub type MarkupId = u32;
87
88#[derive(Clone, Debug, PartialEq, Eq)]
89pub struct Program {
90    pub id: SymId,
91    pub options: ProgramOptions,
92    pub initial: bool,
93    pub recursive: bool,
94    pub storage: Storage,
95    pub items: Vec<Item>,
96    pub paragraphs: Vec<Paragraph>,
97    /// The first paragraph after DECLARATIVES, where a run starts.
98    pub procedure_start: ParaId,
99    pub ranges: Vec<Range>,
100    pub blocks: Vec<Block>,
101    pub places: Vec<Place>,
102    pub exprs: Vec<Expr>,
103    pub conds: Vec<Cond>,
104    pub consts: Vec<Const>,
105    pub plans: Plans,
106    pub services: Services,
107    pub sql: Vec<SqlEntry>,
108    pub abends: Vec<AbendText>,
109    pub edits: Vec<Edit>,
110    pub symbols: Vec<String>,
111    pub debug: Debug,
112}
113
114#[derive(Clone, Debug, PartialEq, Eq)]
115pub struct ProgramOptions {
116    pub options: numeric::Options,
117    pub ssrange: bool,
118    /// The CBL and PROCESS cards as written.
119    pub cards: Vec<String>,
120    pub collating: Collating,
121    /// DECIMAL-POINT IS COMMA: numeric editing shows a comma for the decimal point, and the NUMVAL
122    /// and TEST-NUMVAL functions read one.
123    pub decimal_point_comma: bool,
124    /// The cs NUMVAL-C and TEST-NUMVAL-C take without argument-2 (assumption C102).
125    pub numval_currency: String,
126    /// What FUNCTION WHEN-COMPILED gives; None in a program that does not use it, so its module
127    /// does not depend on when it was compiled.
128    pub when_compiled: Option<CompileTime>,
129}
130
131/// An edited PICTURE's symbols, and the currency sign value its currency symbol stands for, empty
132/// when it has none.
133#[derive(Clone, Debug, PartialEq, Eq)]
134pub struct Edit {
135    pub syms: Vec<Sym>,
136    pub currency: String,
137}
138
139/// When the program was compiled, which FUNCTION WHEN-COMPILED gives: seconds since
140/// 1970-01-01T00:00:00Z and hundredths, and where the time came from.
141#[derive(Clone, Copy, Debug, PartialEq, Eq)]
142pub struct CompileTime {
143    pub seconds: i64,
144    pub hundredths: u32,
145    pub source: TimeSource,
146}
147
148#[derive(Clone, Copy, Debug, PartialEq, Eq)]
149pub enum TimeSource {
150    /// The build's SOURCE_DATE_EPOCH, whole seconds (reproducible-builds.org/specs/source-date-epoch).
151    SourceDateEpoch,
152    Clock,
153}
154
155impl CompileTime {
156    /// 9999-12-31T23:59:59Z, the last second WHEN-COMPILED's four-digit year can show.
157    pub const LATEST: i64 = 253_402_300_799;
158}
159
160#[derive(Clone, Debug, Default, PartialEq, Eq)]
161pub struct Storage {
162    pub size: u32,
163    /// The slab and LOCAL-STORAGE as VALUE clauses leave them.
164    pub image: Vec<u8>,
165    pub local_image: Vec<u8>,
166    /// What VALUE initialization prints (TRUNC(OPT) reports), and its abend.
167    pub init_reports: Vec<SymId>,
168    pub init_abend: Option<AbendId>,
169    /// Each LINKAGE record's size; USING and RETURNING as record ordinals.
170    pub linkage: Vec<u32>,
171    pub using: Vec<u16>,
172    pub returning: Option<u16>,
173    /// Offset and size of each file's record area in the slab.
174    pub file_areas: Vec<(u32, u32)>,
175    /// PARMCHECK's buffer in the slab: offset and size.
176    pub parmcheck: Option<(u32, u32)>,
177}
178
179/// A data item for `dump` and a debugger; no executor reads it.
180#[derive(Clone, Debug, PartialEq, Eq)]
181pub struct Item {
182    pub name: Option<SymId>,
183    pub level: u8,
184    pub parent: Option<u32>,
185    pub offset: u32,
186    pub size: u32,
187    pub occurs: u32,
188    /// Stride and count of each OCCURS on the item and its ancestors, outermost first.
189    pub dims: Vec<(u32, u32)>,
190    pub kind: Kind,
191    pub local: bool,
192    pub linkage: Option<u16>,
193    pub redefines: Option<SymId>,
194    /// The OCCURS DEPENDING ON object's item index.
195    pub depending_on: Option<u32>,
196    /// Each key's item index, true for ASCENDING.
197    pub keys: Vec<(bool, u32)>,
198    pub at: DebugId,
199}
200
201/// `at` is None where the op or terminator that raises the abend gives its position, and the data
202/// entry's for `Storage.init_abend`.
203#[derive(Clone, Debug, PartialEq, Eq)]
204pub struct AbendText {
205    pub code: AbendCode,
206    pub message: SymId,
207    pub at: Option<DebugId>,
208}
209
210#[derive(Clone, Copy, Debug, PartialEq, Eq)]
211pub struct Paragraph {
212    pub name: SymId,
213    pub is_section: bool,
214    pub entry: BlockId,
215    /// The last paragraph of its section.
216    pub section_end: ParaId,
217    /// Its section's priority-number, 0 for none; 50 or more is an independent segment.
218    pub priority: u8,
219    pub at: DebugId,
220    /// On a paragraph that ends a range: the IRONWORK abend when control passes its end while it
221    /// holds the return point of a frame control left that cannot resume (C99).
222    pub abandoned: Option<AbendId>,
223}
224
225#[derive(Clone, Debug, PartialEq, Eq)]
226pub struct Block {
227    pub ops: Vec<Op>,
228    pub end: Terminator,
229}
230
231/// The plan tables ops index; a MOVE's plan sits in its op.
232#[derive(Clone, Debug, Default, PartialEq, Eq)]
233pub struct Plans {
234    pub arith: Vec<ArithPlan>,
235    pub init: Vec<InitPlan>,
236    pub display: Vec<DisplayPlan>,
237    pub inspect: Vec<InspectPlan>,
238    pub string: Vec<StringPlan>,
239    pub unstring: Vec<UnstringPlan>,
240    pub search_all: Vec<SearchAllPlan>,
241    pub function: Vec<FunctionPlan>,
242}
243
244#[derive(Clone, Debug, Default, PartialEq, Eq)]
245pub struct Services {
246    pub file_ops: Vec<FileOp>,
247    pub files: Vec<FileDesc>,
248    pub calls: Vec<CallPlan>,
249    pub sorts: Vec<SortPlan>,
250    pub releases: Vec<ReleasePlan>,
251    pub returns: Vec<ReturnPlan>,
252    pub invokes: Vec<InvokePlan>,
253    pub cics: Vec<CicsCommand>,
254    pub sqlca: Sqlca,
255    /// The ENTRY statements, in source order, which a CALL of their names enters.
256    pub entries: Vec<EntryPoint>,
257    /// A class definition's data and methods; None for any other program.
258    pub class: Option<Box<Class>>,
259    /// The invocations of user-defined functions, by `Operand::UserFunction`.
260    pub user_functions: Vec<UserFunctionPlan>,
261    /// A user-defined function's definition; None for any other program.
262    pub function: Option<FunctionDefinition>,
263    pub declaratives: Declaratives,
264    /// JSON GENERATE, JSON PARSE, XML GENERATE and XML PARSE, by `Op::Markup`.
265    pub markup: Vec<Markup>,
266    /// The REPORT SECTION's reports, which `Op::Report` names by index.
267    pub report: ReportWriter,
268    /// EXTERNAL and GLOBAL storage, files and procedures.
269    pub scope: Scope,
270}
271
272// The whole program as one value, the files' data items last, as the load module's LIR section
273// carries them after its records (load-module.md ยง3.4).
274impl crate::module::codec::Encode for Program {
275    fn encode(&self, w: &mut crate::module::codec::Writer) {
276        let Program { id, options, initial, recursive, storage, items, paragraphs, procedure_start, ranges, blocks, places, exprs, conds, consts, plans, services, sql, abends, edits, symbols, debug } = self;
277        id.encode(w);
278        options.encode(w);
279        initial.encode(w);
280        recursive.encode(w);
281        storage.encode(w);
282        items.encode(w);
283        paragraphs.encode(w);
284        procedure_start.encode(w);
285        ranges.encode(w);
286        blocks.encode(w);
287        places.encode(w);
288        exprs.encode(w);
289        conds.encode(w);
290        consts.encode(w);
291        plans.encode(w);
292        services.encode(w);
293        sql.encode(w);
294        abends.encode(w);
295        edits.encode(w);
296        symbols.encode(w);
297        debug.encode(w);
298        let assigned: Vec<(u32, AssignItem)> = services.files.iter().enumerate().filter_map(|(k, f)| Some((k as u32, f.assign_item?))).collect();
299        assigned.encode(w);
300    }
301}
302
303impl crate::module::codec::Decode for Program {
304    fn decode(r: &mut crate::module::codec::Reader<'_>) -> Result<Self, crate::module::ModuleError> {
305        use crate::module::codec::Decode;
306        let at = r.position();
307        let mut program = Program {
308            id: Decode::decode(r)?,
309            options: Decode::decode(r)?,
310            initial: Decode::decode(r)?,
311            recursive: Decode::decode(r)?,
312            storage: Decode::decode(r)?,
313            items: Decode::decode(r)?,
314            paragraphs: Decode::decode(r)?,
315            procedure_start: Decode::decode(r)?,
316            ranges: Decode::decode(r)?,
317            blocks: Decode::decode(r)?,
318            places: Decode::decode(r)?,
319            exprs: Decode::decode(r)?,
320            conds: Decode::decode(r)?,
321            consts: Decode::decode(r)?,
322            plans: Decode::decode(r)?,
323            services: Decode::decode(r)?,
324            sql: Decode::decode(r)?,
325            abends: Decode::decode(r)?,
326            edits: Decode::decode(r)?,
327            symbols: Decode::decode(r)?,
328            debug: Decode::decode(r)?,
329        };
330        for (k, item) in Vec::<(u32, AssignItem)>::decode(r)? {
331            let file = program.services.files.get_mut(k as usize).ok_or_else(|| r.malformed(at, format!("an assign item for file {k}")))?;
332            file.assign_item = Some(item);
333        }
334        program_valid(&program).map_err(|reason| r.malformed(at, reason))?;
335        Ok(program)
336    }
337}
338codec_struct!(ProgramOptions { options, ssrange, cards, collating, decimal_point_comma, numval_currency, when_compiled });
339codec_struct!(Edit { syms, currency });
340codec_struct!(CompileTime { seconds, hundredths, source } check compile_time_valid);
341codec_enum!(TimeSource { SourceDateEpoch = 0, Clock = 1 });
342codec_struct!(Storage {
343    size, image, local_image, init_reports, init_abend, linkage, using, returning, file_areas, parmcheck,
344} check storage_valid);
345codec_struct!(Item {
346    name, level, parent, offset, size, occurs, dims, kind, local, linkage, redefines, depending_on, keys, at,
347});
348codec_struct!(AbendText { code, message, at });
349codec_struct!(Paragraph { name, is_section, entry, section_end, priority, at, abandoned });
350codec_struct!(Block { ops, end });
351codec_struct!(Plans { arith, init, display, inspect, string, unstring, search_all, function });
352codec_struct!(Services {
353    file_ops, files, calls, sorts, releases, returns, invokes, cics, sqlca, entries, class, user_functions, function, declaratives, markup, report, scope,
354});
355
356pub(crate) fn program_valid(program: &Program) -> Result<(), String> {
357    sql::table_valid(&program.sql, &program.symbols)
358}
359
360fn compile_time_valid(t: &CompileTime) -> Result<(), String> {
361    let whole = t.source == TimeSource::SourceDateEpoch && t.hundredths != 0;
362    if !(0..=CompileTime::LATEST).contains(&t.seconds) || t.hundredths > 99 || whole {
363        return Err(format!("a compile time of {} seconds and {} hundredths from {:?}", t.seconds, t.hundredths, t.source));
364    }
365    Ok(())
366}
367
368fn storage_valid(storage: &Storage) -> Result<(), String> {
369    if u32::try_from(storage.image.len()) != Ok(storage.size) {
370        return Err(format!("an image of {} bytes for a slab of {}", storage.image.len(), storage.size));
371    }
372    match storage.parmcheck {
373        Some((offset, len)) if offset.checked_add(len).is_none_or(|end| end > storage.size) => Err(format!("a PARMCHECK buffer at {offset} for {len} in a slab of {}", storage.size)),
374        _ => Ok(()),
375    }
376}