ironwork_compile/
printer.rs1use crate::layout::{Layout, Resolved};
9use syntax::Error;
10use syntax::ast::*;
11
12#[derive(Clone, Copy, Debug, PartialEq, Eq)]
14pub struct Carriage {
15 pub machine: bool,
17 pub reserved: bool,
19}
20
21#[derive(Clone, Copy, Debug, PartialEq, Eq)]
23pub enum Space {
24 Lines(u64),
26 Channel(u8),
28 PageMode,
30}
31
32pub fn mnemonic_space(environment: &str) -> Option<Space> {
34 match environment {
35 "CSP" => Some(Space::Lines(0)),
36 "AFP-5A" => Some(Space::PageMode),
37 c if c.len() == 3 && c.starts_with('C') => c[1..].parse().ok().filter(|n| (1..=12).contains(n)).map(Space::Channel),
38 _ => None,
39 }
40}
41
42pub(crate) fn carriages(program: &Program, layout: &Layout, adv: bool) -> Vec<Option<Carriage>> {
44 let mut uses = vec![(false, false); program.files.len()];
45 let mut pending: Vec<&[Stmt]> = program.paragraphs.iter().map(|p| p.statements.as_slice()).collect();
46 while let Some(stmts) = pending.pop() {
47 for s in stmts {
48 pending.extend(crate::oo::bodies(s));
49 if let Stmt::Write { record, advancing: Some(a), .. } = s
50 && let Some(k) = file_of(layout, record)
51 {
52 uses[k] = (true, uses[k].1 || a.before());
53 }
54 }
55 }
56 program
57 .files
58 .iter()
59 .zip(uses)
60 .map(|(f, (advancing, before))| {
61 let print = f.organization == Organization::Sequential && !f.sort && (advancing || f.linage.is_some() || !f.reports.is_empty());
62 print.then_some(Carriage { machine: before, reserved: reserves_first_byte(f, adv) })
63 })
64 .collect()
65}
66
67pub(crate) fn reserves_first_byte(f: &FileDecl, adv: bool) -> bool {
70 !adv && f.linage.is_none() && f.organization == Organization::Sequential && !f.sort
71}
72
73fn file_of(layout: &Layout, record: &Ref) -> Option<usize> {
74 match layout.resolve(&record.name, &record.qualifiers, record.pos) {
75 Ok(Resolved::Item(i)) => layout.items[i].file.map(usize::from),
76 _ => None,
77 }
78}
79
80pub(crate) fn check_write(program: &Program, layout: &Layout, record: &Ref, advancing: &Advancing, pos: syntax::Pos, errors: &mut Vec<Error>) {
82 if let Advancing::Mnemonic { name, environment, .. } = advancing
83 && mnemonic_space(environment).is_none()
84 {
85 errors.push(syntax::messages::IWR0013.at(pos, format!("ADVANCING {name}: stacker selection ({environment}) on a card punch is not supported yet")));
86 }
87 let Some(f) = file_of(layout, record).map(|k| &program.files[k]) else { return };
88 match f.organization {
89 Organization::Indexed | Organization::Relative => errors.push(syntax::messages::IWC0144.at(pos, format!("WRITE ... ADVANCING: {} is not a sequential file", f.name))),
90 Organization::LineSequential if advancing.before() || matches!(advancing, Advancing::Mnemonic { .. }) => {
91 errors.push(syntax::messages::IWC0145.at(pos, format!("WRITE ... BEFORE ADVANCING, or ADVANCING a mnemonic-name, is not allowed for the line-sequential file {}", f.name)));
92 }
93 _ => {}
94 }
95}