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, 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
272codec_struct!(Program {
273    id, options, initial, recursive, storage, items, paragraphs, procedure_start, ranges, blocks, places, exprs,
274    conds, consts, plans, services, sql, abends, edits, symbols, debug,
275} check program_valid);
276codec_struct!(ProgramOptions { options, ssrange, cards, collating, decimal_point_comma, numval_currency, when_compiled });
277codec_struct!(Edit { syms, currency });
278codec_struct!(CompileTime { seconds, hundredths, source } check compile_time_valid);
279codec_enum!(TimeSource { SourceDateEpoch = 0, Clock = 1 });
280codec_struct!(Storage {
281    size, image, local_image, init_reports, init_abend, linkage, using, returning, file_areas, parmcheck,
282} check storage_valid);
283codec_struct!(Item {
284    name, level, parent, offset, size, occurs, dims, kind, local, linkage, redefines, depending_on, keys, at,
285});
286codec_struct!(AbendText { code, message, at });
287codec_struct!(Paragraph { name, is_section, entry, section_end, priority, at, abandoned });
288codec_struct!(Block { ops, end });
289codec_struct!(Plans { arith, init, display, inspect, string, unstring, search_all, function });
290codec_struct!(Services {
291    file_ops, files, calls, sorts, releases, returns, invokes, cics, sqlca, entries, class, user_functions, function, declaratives, markup, report, scope,
292});
293
294pub(crate) fn program_valid(program: &Program) -> Result<(), String> {
295    sql::table_valid(&program.sql, &program.symbols)
296}
297
298fn compile_time_valid(t: &CompileTime) -> Result<(), String> {
299    let whole = t.source == TimeSource::SourceDateEpoch && t.hundredths != 0;
300    if !(0..=CompileTime::LATEST).contains(&t.seconds) || t.hundredths > 99 || whole {
301        return Err(format!("a compile time of {} seconds and {} hundredths from {:?}", t.seconds, t.hundredths, t.source));
302    }
303    Ok(())
304}
305
306fn storage_valid(storage: &Storage) -> Result<(), String> {
307    if u32::try_from(storage.image.len()) != Ok(storage.size) {
308        return Err(format!("an image of {} bytes for a slab of {}", storage.image.len(), storage.size));
309    }
310    match storage.parmcheck {
311        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)),
312        _ => Ok(()),
313    }
314}