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