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