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 syntax::Error::at(e.pos(), e.to_string())
84 }
85}
86
87type R<T> = Result<T, LowerError>;
88
89fn unsupported<T>(what: &'static str, pos: Pos) -> R<T> {
90 Err(LowerError::Unsupported(what, pos))
91}
92
93fn next_id<T>(table: &[T], what: &'static str) -> R<u32> {
95 u32::try_from(table.len()).map_err(|_| LowerError::Exceeds(what, Pos::default()))
96}
97
98fn push<T>(table: &mut Vec<T>, value: T, what: &'static str) -> R<u32> {
99 let id = next_id(table, what)?;
100 table.push(value);
101 Ok(id)
102}
103
104pub fn lower(compiled: &Compiled) -> Result<lir::Program, LowerError> {
107 let mut l = Lower::new(compiled);
108 let id = l.sym(&compiled.program.id);
109 let sources = compiled.program.sources.iter().map(|s| l.sym(s)).collect();
110 let storage = l.storage()?;
111 let items = l.items()?;
112 l.services.files = l.files()?;
113 l.services.declaratives = l.declaratives()?;
114 l.services.scope = l.scope()?;
115 l.services.report = l.report_writer()?;
116 (l.sql, l.services.sqlca) = l.sql_table()?;
117 let paragraphs = l.procedure()?;
118 l.unresolved_statuses()?;
119 l.services.entries = l.entry_points()?;
120 l.services.class = l.class_definition()?;
121 l.services.function = l.function_definition()?;
122 let procedure_start = compiled.program.report_writer.procedure_start.min(compiled.program.paragraphs.len());
123 let (blocks, debug) = l.blocks.finish()?;
124 let program = lir::Program {
125 id,
126 options: lir::ProgramOptions {
127 options: compiled.options,
128 ssrange: compiled.ssrange,
129 cards: compiled.program.options.clone(),
130 collating: collating(&compiled.collating),
131 decimal_point_comma: compiled.program.environment.decimal_point_comma,
132 numval_currency: crate::machine::numval_currency(&compiled.program.environment.currency),
133 when_compiled: l.plans.function.iter().any(|f| f.func == lir::Func::WhenCompiled).then_some(compiled.when_compiled),
134 },
135 initial: compiled.program.initial,
136 recursive: compiled.program.recursive,
137 storage,
138 items,
139 paragraphs,
140 procedure_start: procedure_start as u32,
141 ranges: l.ranges,
142 blocks,
143 places: l.places,
144 exprs: l.exprs,
145 conds: l.conds,
146 consts: l.consts,
147 plans: l.plans,
148 services: l.services,
149 sql: l.sql,
150 abends: l.abends,
151 edits: edits(&compiled.layout)?,
152 symbols: l.symbols,
153 debug: lir::Debug { sources, positions: l.positions, ops: debug.ops, statements: debug.statements },
154 };
155 if cfg!(debug_assertions) {
156 verify(&program).map_err(LowerError::Invalid)?;
157 }
158 Ok(program)
159}
160
161struct Lower<'c> {
163 c: &'c Compiled,
164 layout: &'c Layout,
165 program: &'c ast::Program,
166 page: &'static CodePage,
167 symbols: Vec<String>,
168 symbol_ids: HashMap<String, SymId>,
169 positions: Vec<Pos>,
170 position_ids: HashMap<(u16, u32, u32), DebugId>,
171 places: Vec<lir::Place>,
172 place_items: Vec<Option<usize>>,
174 place_ids: HashMap<String, PlaceId>,
175 exprs: Vec<lir::Expr>,
176 conds: Vec<lir::Cond>,
177 consts: Vec<lir::Const>,
178 const_ids: HashMap<String, ConstId>,
179 abends: Vec<lir::AbendText>,
180 abend_ids: HashMap<String, AbendId>,
181 plans: lir::Plans,
182 services: lir::Services,
183 sql: Vec<lir::SqlEntry>,
185 ranges: Vec<lir::Range>,
186 range_ids: HashMap<(u32, u32, lir::RangeKind), RangeId>,
187 blocks: flow::Blocks,
188 temps: u16,
189 entries: Vec<BlockId>,
191 sentences: HashMap<(usize, usize), BlockId>,
193 entry_blocks: HashMap<(usize, usize), BlockId>,
195 altered: BTreeSet<usize>,
197 segments: bool,
200 debugging: bool,
202 within: data::Within,
205}
206
207impl<'c> Lower<'c> {
208 fn new(c: &'c Compiled) -> Self {
209 Self {
210 c,
211 layout: &c.layout,
212 program: &c.program,
213 page: c.options.code_page(),
214 symbols: Vec::new(),
215 symbol_ids: HashMap::new(),
216 positions: Vec::new(),
217 position_ids: HashMap::new(),
218 places: Vec::new(),
219 place_items: Vec::new(),
220 place_ids: HashMap::new(),
221 exprs: Vec::new(),
222 conds: Vec::new(),
223 consts: Vec::new(),
224 const_ids: HashMap::new(),
225 abends: Vec::new(),
226 abend_ids: HashMap::new(),
227 plans: lir::Plans::default(),
228 services: lir::Services::default(),
229 sql: Vec::new(),
230 ranges: Vec::new(),
231 range_ids: HashMap::new(),
232 blocks: flow::Blocks::default(),
233 temps: 0,
234 entries: Vec::new(),
235 sentences: HashMap::new(),
236 entry_blocks: HashMap::new(),
237 altered: BTreeSet::new(),
238 segments: false,
239 debugging: !c.declaratives.triggers.is_empty(),
240 within: data::Within::Own,
241 }
242 }
243
244 fn sym(&mut self, text: &str) -> SymId {
245 if let Some(&id) = self.symbol_ids.get(text) {
246 return id;
247 }
248 let id = self.symbols.len() as SymId;
249 self.symbols.push(text.to_owned());
250 self.symbol_ids.insert(text.to_owned(), id);
251 id
252 }
253
254 fn at(&mut self, pos: Pos) -> DebugId {
255 let key = (pos.file, pos.line, pos.col);
256 if let Some(&id) = self.position_ids.get(&key) {
257 return id;
258 }
259 let id = self.positions.len() as DebugId;
260 self.positions.push(pos);
261 self.position_ids.insert(key, id);
262 id
263 }
264
265 fn abend(&mut self, code: AbendCode, message: &str, pos: Option<Pos>) -> R<AbendId> {
267 let at = pos.map(|p| self.at(p));
268 let key = format!("{code:?}{at:?}{message}");
269 if let Some(&id) = self.abend_ids.get(&key) {
270 return Ok(id);
271 }
272 let text = lir::AbendText { code, message: self.sym(message), at };
273 let id = push(&mut self.abends, text, "abend messages")?;
274 self.abend_ids.insert(key, id);
275 Ok(id)
276 }
277
278 fn ironwork(&mut self, message: &str) -> R<AbendId> {
279 self.abend(AbendCode::Ironwork, message, None)
280 }
281
282 fn storage(&mut self) -> R<lir::Storage> {
285 let layout = self.layout;
286 let (size, local) = (layout.size as usize, layout.local_size as usize);
287 let (mut out, mut err) = (Vec::new(), Vec::new());
288 let (image, local_image, abend) = {
289 let mut unit = RunUnit::new(Library::default(), crate::files::Dds::default(), None, Clock::Fixed(0, 0), &mut out, &mut err);
290 let me = unit.add(None, self.program, size);
291 let abend = Machine::unbound(self.c, me, &mut unit).err();
292 let base = unit.programs[me].base;
293 let image = unit.mem[base..base + size].to_vec();
294 let local_image = if local > 0 { unit.mem[unit.mem.len() - local..].to_vec() } else { Vec::new() };
295 (image, local_image, abend)
296 };
297 let init_reports = String::from_utf8_lossy(&err).lines().map(|l| self.sym(l)).collect();
298 let init_abend = match abend {
299 Some(a) => Some(self.abend(a.code.clone(), &a.message, Some(a.pos))?),
300 None => None,
301 };
302 let root = |name: &str| layout.linkage_roots.iter().position(|&i| layout.items[i].name.as_deref() == Some(name));
303 let mut using = Vec::new();
304 for ast::Param { by_value: _, name } in &self.program.using {
306 match root(name).map(u16::try_from) {
307 Some(Ok(ordinal)) => using.push(ordinal),
308 Some(Err(_)) => return Err(LowerError::Exceeds("LINKAGE records", Pos::default())),
309 None => return unsupported("PROCEDURE DIVISION USING an item that is not a LINKAGE record", Pos::default()),
310 }
311 }
312 let returning = match &self.program.returning {
313 None => None,
314 Some(name) => match root(name).map(u16::try_from) {
315 Some(Ok(ordinal)) => Some(ordinal),
316 Some(Err(_)) => return Err(LowerError::Exceeds("LINKAGE records", Pos::default())),
317 None => return unsupported("RETURNING an item that is not a LINKAGE record", Pos::default()),
318 },
319 };
320 Ok(lir::Storage {
321 size: layout.size,
322 image,
323 local_image,
324 init_reports,
325 init_abend,
326 linkage: layout.linkage_roots.iter().map(|&i| layout.items[i].size).collect(),
327 using,
328 returning,
329 file_areas: layout.file_areas.clone(),
330 parmcheck: layout.parmcheck,
331 })
332 }
333
334 fn items(&mut self) -> R<Vec<lir::Item>> {
336 let layout = self.layout;
337 let mut items = Vec::with_capacity(layout.items.len());
338 for item in &layout.items {
339 let item_of = |r: &ast::Ref| match layout.resolve(&r.name, &r.qualifiers, r.pos) {
340 Ok(Resolved::Item(i)) => Ok(i as u32),
341 _ => unsupported("an OCCURS DEPENDING ON or KEY that names no data item", r.pos),
342 };
343 let depending_on = item.depending_on.as_ref().map(item_of).transpose()?;
344 let keys = item.keys.iter().map(|(ascending, r)| item_of(r).map(|i| (*ascending, i))).collect::<R<_>>()?;
345 items.push(lir::Item {
346 name: item.name.as_deref().map(|n| self.sym(n)),
347 level: item.level,
348 parent: item.parent.map(|p| p as u32),
349 offset: item.offset,
350 size: item.size,
351 occurs: item.occurs,
352 dims: item.dims.clone(),
353 kind: item.kind,
354 local: item.local,
355 linkage: item.linkage,
356 redefines: item.redefines.as_deref().map(|n| self.sym(n)),
357 depending_on,
358 keys,
359 at: self.at(item.pos),
360 });
361 }
362 Ok(items)
363 }
364}
365
366fn collating(sequence: &crate::collating::Sequence) -> lir::Collating {
369 if sequence.is_native() {
370 return lir::Collating::Native;
371 }
372 let characters = (1..=sequence.count() as i64).filter_map(|k| sequence.character(k)).collect();
373 lir::Collating::Sequence(lir::Sequence {
374 positions: Box::new(sequence.positions()),
375 characters,
376 high_value: sequence.high_value,
377 low_value: sequence.low_value,
378 })
379}
380
381fn edits(layout: &Layout) -> R<Vec<lir::Edit>> {
383 if layout.edits.len() != layout.currencies.len() {
384 return Err(LowerError::Invalid(format!("{} edited PICTUREs with {} currency values", layout.edits.len(), layout.currencies.len())));
385 }
386 Ok(layout.edits.iter().zip(&layout.currencies).map(|(syms, currency)| lir::Edit { syms: syms.clone(), currency: currency.clone() }).collect())
387}
388
389fn is_static(place: &lir::Place) -> bool {
392 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()
393}