1mod call;
14mod check;
15mod cics;
16mod class;
17mod cond;
18mod data;
19mod file;
20mod flow;
21mod function;
22mod markup;
23mod plans;
24mod report;
25mod scope;
26mod search;
27mod set;
28mod sort;
29mod sql;
30mod text;
31mod user_function;
32mod verify;
33
34#[cfg(test)]
35mod tests;
36
37pub use verify::verify;
38
39use crate::Compiled;
40use crate::layout::{Layout, Resolved};
41use crate::machine::Machine;
42use crate::unit::{AddProgram, Clock, Library, RunUnit};
43use rt::abend::AbendCode;
44use rt::lir::{self, AbendId, BlockId, ConstId, DebugId, PlaceId, RangeId, SymId};
45use std::collections::{BTreeSet, HashMap};
46use std::fmt;
47use syntax::Pos;
48use syntax::ast;
49use zarch::ebcdic::CodePage;
50
51#[derive(Clone, Debug, PartialEq, Eq)]
53pub enum LowerError {
54 Unsupported(&'static str, Pos),
56 Exceeds(&'static str, Pos),
58 Invalid(String),
60}
61
62impl LowerError {
63 pub fn pos(&self) -> Pos {
64 match self {
65 Self::Unsupported(_, pos) | Self::Exceeds(_, pos) => *pos,
66 Self::Invalid(_) => Pos::default(),
67 }
68 }
69}
70
71impl fmt::Display for LowerError {
72 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73 match self {
74 Self::Unsupported(what, _) => write!(f, "lowering: {what} is not lowered yet"),
75 Self::Exceeds(what, _) => write!(f, "lowering: {what} exceeds the LIR's limit"),
76 Self::Invalid(why) => write!(f, "lowering: the lowered program is invalid: {why}"),
77 }
78 }
79}
80
81impl From<LowerError> for syntax::Error {
82 fn from(e: LowerError) -> Self {
83 let message = match e {
84 LowerError::Unsupported(..) => syntax::messages::IWR0052,
85 LowerError::Exceeds(..) => syntax::messages::IWL0001,
86 LowerError::Invalid(_) => syntax::messages::IWL0002,
87 };
88 message.at(e.pos(), e.to_string())
89 }
90}
91
92type R<T> = Result<T, LowerError>;
93
94fn unsupported<T>(what: &'static str, pos: Pos) -> R<T> {
95 Err(LowerError::Unsupported(what, pos))
96}
97
98fn next_id<T>(table: &[T], what: &'static str) -> R<u32> {
100 u32::try_from(table.len()).map_err(|_| LowerError::Exceeds(what, Pos::default()))
101}
102
103fn push<T>(table: &mut Vec<T>, value: T, what: &'static str) -> R<u32> {
104 let id = next_id(table, what)?;
105 table.push(value);
106 Ok(id)
107}
108
109pub fn lower(compiled: &Compiled) -> Result<lir::Program, LowerError> {
112 let mut l = Lower::new(compiled);
113 let id = l.sym(&compiled.program.id);
114 let sources = compiled.program.sources.iter().map(|s| l.sym(s)).collect();
115 let storage = l.storage()?;
116 let items = l.items()?;
117 l.services.files = l.files()?;
118 l.services.declaratives = l.declaratives()?;
119 l.services.scope = l.scope()?;
120 l.services.report = l.report_writer()?;
121 (l.sql, l.services.sqlca) = l.sql_table()?;
122 let paragraphs = l.procedure()?;
123 l.unresolved_statuses()?;
124 l.services.entries = l.entry_points()?;
125 l.services.class = l.class_definition()?;
126 l.services.function = l.function_definition()?;
127 let procedure_start = compiled.program.report_writer.procedure_start.min(compiled.program.paragraphs.len());
128 let (blocks, debug) = l.blocks.finish()?;
129 let program = lir::Program {
130 id,
131 options: lir::ProgramOptions {
132 options: compiled.options,
133 ssrange: compiled.ssrange,
134 cards: compiled.program.options.clone(),
135 collating: collating(&compiled.collating),
136 decimal_point_comma: compiled.program.environment.decimal_point_comma,
137 numval_currency: crate::machine::numval_currency(&compiled.program.environment.currency),
138 when_compiled: l.plans.function.iter().any(|f| f.func == lir::Func::WhenCompiled).then_some(compiled.when_compiled),
139 },
140 initial: compiled.program.initial,
141 recursive: compiled.program.recursive,
142 storage,
143 items,
144 paragraphs,
145 procedure_start: procedure_start as u32,
146 ranges: l.ranges,
147 blocks,
148 places: l.places,
149 exprs: l.exprs,
150 conds: l.conds,
151 consts: l.consts,
152 plans: l.plans,
153 services: l.services,
154 sql: l.sql,
155 abends: l.abends,
156 edits: edits(&compiled.layout)?,
157 symbols: l.symbols,
158 debug: lir::Debug { sources, positions: l.positions, ops: debug.ops, statements: debug.statements },
159 };
160 if cfg!(debug_assertions) {
161 verify(&program).map_err(LowerError::Invalid)?;
162 }
163 Ok(program)
164}
165
166struct Lower<'c> {
168 c: &'c Compiled,
169 layout: &'c Layout,
170 program: &'c ast::Program,
171 page: &'static CodePage,
172 symbols: Vec<String>,
173 symbol_ids: HashMap<String, SymId>,
174 positions: Vec<Pos>,
175 position_ids: HashMap<(u16, u32, u32), DebugId>,
176 places: Vec<lir::Place>,
177 place_items: Vec<Option<usize>>,
179 place_ids: HashMap<String, PlaceId>,
180 exprs: Vec<lir::Expr>,
181 conds: Vec<lir::Cond>,
182 consts: Vec<lir::Const>,
183 const_ids: HashMap<String, ConstId>,
184 abends: Vec<lir::AbendText>,
185 abend_ids: HashMap<String, AbendId>,
186 plans: lir::Plans,
187 services: lir::Services,
188 sql: Vec<lir::SqlEntry>,
190 ranges: Vec<lir::Range>,
191 range_ids: HashMap<(u32, u32, lir::RangeKind), RangeId>,
192 blocks: flow::Blocks,
193 temps: u16,
194 entries: Vec<BlockId>,
196 sentences: HashMap<(usize, usize), BlockId>,
198 entry_blocks: HashMap<(usize, usize), BlockId>,
200 altered: BTreeSet<usize>,
202 segments: bool,
205 debugging: bool,
207 within: data::Within,
210}
211
212impl<'c> Lower<'c> {
213 fn new(c: &'c Compiled) -> Self {
214 Self {
215 c,
216 layout: &c.layout,
217 program: &c.program,
218 page: c.options.code_page(),
219 symbols: Vec::new(),
220 symbol_ids: HashMap::new(),
221 positions: Vec::new(),
222 position_ids: HashMap::new(),
223 places: Vec::new(),
224 place_items: Vec::new(),
225 place_ids: HashMap::new(),
226 exprs: Vec::new(),
227 conds: Vec::new(),
228 consts: Vec::new(),
229 const_ids: HashMap::new(),
230 abends: Vec::new(),
231 abend_ids: HashMap::new(),
232 plans: lir::Plans::default(),
233 services: lir::Services::default(),
234 sql: Vec::new(),
235 ranges: Vec::new(),
236 range_ids: HashMap::new(),
237 blocks: flow::Blocks::default(),
238 temps: 0,
239 entries: Vec::new(),
240 sentences: HashMap::new(),
241 entry_blocks: HashMap::new(),
242 altered: BTreeSet::new(),
243 segments: false,
244 debugging: !c.declaratives.triggers.is_empty(),
245 within: data::Within::Own,
246 }
247 }
248
249 fn sym(&mut self, text: &str) -> SymId {
250 if let Some(&id) = self.symbol_ids.get(text) {
251 return id;
252 }
253 let id = self.symbols.len() as SymId;
254 self.symbols.push(text.to_owned());
255 self.symbol_ids.insert(text.to_owned(), id);
256 id
257 }
258
259 fn at(&mut self, pos: Pos) -> DebugId {
260 let key = (pos.file, pos.line, pos.col);
261 if let Some(&id) = self.position_ids.get(&key) {
262 return id;
263 }
264 let id = self.positions.len() as DebugId;
265 self.positions.push(pos);
266 self.position_ids.insert(key, id);
267 id
268 }
269
270 fn abend(&mut self, code: AbendCode, message: &str, pos: Option<Pos>) -> R<AbendId> {
272 let at = pos.map(|p| self.at(p));
273 let key = format!("{code:?}{at:?}{message}");
274 if let Some(&id) = self.abend_ids.get(&key) {
275 return Ok(id);
276 }
277 let text = lir::AbendText { code, message: self.sym(message), at };
278 let id = push(&mut self.abends, text, "abend messages")?;
279 self.abend_ids.insert(key, id);
280 Ok(id)
281 }
282
283 fn ironwork(&mut self, message: &str) -> R<AbendId> {
284 self.abend(AbendCode::Ironwork, message, None)
285 }
286
287 fn storage(&mut self) -> R<lir::Storage> {
290 let layout = self.layout;
291 let (size, local) = (layout.size as usize, layout.local_size as usize);
292 let (mut out, mut err) = (Vec::new(), Vec::new());
293 let (image, local_image, abend) = {
294 let mut unit = RunUnit::new(Library::default(), crate::files::Dds::default(), None, Clock::Fixed(0, 0), &mut out, &mut err);
295 let me = unit.add(None, self.program, size);
296 let abend = Machine::unbound(self.c, me, &mut unit).err();
297 let base = unit.programs[me].base;
298 let image = unit.mem[base..base + size].to_vec();
299 let local_image = if local > 0 { unit.mem[unit.mem.len() - local..].to_vec() } else { Vec::new() };
300 (image, local_image, abend)
301 };
302 let init_reports = String::from_utf8_lossy(&err).lines().map(|l| self.sym(l)).collect();
303 let init_abend = match abend {
304 Some(a) => Some(self.abend(a.code.clone(), &a.message, Some(a.pos))?),
305 None => None,
306 };
307 let root = |name: &str| layout.linkage_roots.iter().position(|&i| layout.items[i].name.as_deref() == Some(name));
308 let mut using = Vec::new();
309 for ast::Param { by_value: _, name } in &self.program.using {
311 match root(name).map(u16::try_from) {
312 Some(Ok(ordinal)) => using.push(ordinal),
313 Some(Err(_)) => return Err(LowerError::Exceeds("LINKAGE records", Pos::default())),
314 None => return unsupported("PROCEDURE DIVISION USING an item that is not a LINKAGE record", Pos::default()),
315 }
316 }
317 let returning = match &self.program.returning {
318 None => None,
319 Some(name) => match root(name).map(u16::try_from) {
320 Some(Ok(ordinal)) => Some(ordinal),
321 Some(Err(_)) => return Err(LowerError::Exceeds("LINKAGE records", Pos::default())),
322 None => return unsupported("RETURNING an item that is not a LINKAGE record", Pos::default()),
323 },
324 };
325 Ok(lir::Storage {
326 size: layout.size,
327 image,
328 local_image,
329 init_reports,
330 init_abend,
331 linkage: layout.linkage_roots.iter().map(|&i| layout.items[i].size).collect(),
332 using,
333 returning,
334 file_areas: layout.file_areas.clone(),
335 parmcheck: layout.parmcheck,
336 })
337 }
338
339 fn items(&mut self) -> R<Vec<lir::Item>> {
341 let layout = self.layout;
342 let mut items = Vec::with_capacity(layout.items.len());
343 for item in &layout.items {
344 let item_of = |r: &ast::Ref| match layout.resolve(&r.name, &r.qualifiers, r.pos) {
345 Ok(Resolved::Item(i)) => Ok(i as u32),
346 _ => unsupported("an OCCURS DEPENDING ON or KEY that names no data item", r.pos),
347 };
348 let depending_on = item.depending_on.as_ref().map(item_of).transpose()?;
349 let keys = item.keys.iter().map(|(ascending, r)| item_of(r).map(|i| (*ascending, i))).collect::<R<_>>()?;
350 items.push(lir::Item {
351 name: item.name.as_deref().map(|n| self.sym(n)),
352 level: item.level,
353 parent: item.parent.map(|p| p as u32),
354 offset: item.offset,
355 size: item.size,
356 occurs: item.occurs,
357 dims: item.dims.clone(),
358 kind: item.kind,
359 local: item.local,
360 linkage: item.linkage,
361 redefines: item.redefines.as_deref().map(|n| self.sym(n)),
362 depending_on,
363 keys,
364 at: self.at(item.pos),
365 });
366 }
367 Ok(items)
368 }
369}
370
371fn collating(sequence: &crate::collating::Sequence) -> lir::Collating {
374 if sequence.is_native() {
375 return lir::Collating::Native;
376 }
377 let characters = (1..=sequence.count() as i64).filter_map(|k| sequence.character(k)).collect();
378 lir::Collating::Sequence(lir::Sequence {
379 positions: Box::new(sequence.positions()),
380 characters,
381 high_value: sequence.high_value,
382 low_value: sequence.low_value,
383 })
384}
385
386fn edits(layout: &Layout) -> R<Vec<lir::Edit>> {
388 if layout.edits.len() != layout.currencies.len() {
389 return Err(LowerError::Invalid(format!("{} edited PICTUREs with {} currency values", layout.edits.len(), layout.currencies.len())));
390 }
391 Ok(layout.edits.iter().zip(&layout.currencies).map(|(syms, currency)| lir::Edit { syms: syms.clone(), currency: currency.clone() }).collect())
392}
393
394fn is_static(place: &lir::Place) -> bool {
397 matches!(place.base, lir::Base::Program | lir::Base::Local | lir::Base::ReturnCode) && place.moved.is_empty() && place.subscripts.is_empty() && place.odo.is_empty() && place.refmod.is_none()
398}