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