use crate::layout::{Layout, Resolved};
use syntax::Error;
use syntax::ast::*;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Carriage {
pub machine: bool,
pub reserved: bool,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Space {
Lines(u64),
Channel(u8),
PageMode,
}
pub fn mnemonic_space(environment: &str) -> Option<Space> {
match environment {
"CSP" => Some(Space::Lines(0)),
"AFP-5A" => Some(Space::PageMode),
c if c.len() == 3 && c.starts_with('C') => c[1..].parse().ok().filter(|n| (1..=12).contains(n)).map(Space::Channel),
_ => None,
}
}
pub(crate) fn carriages(program: &Program, layout: &Layout, adv: bool) -> Vec<Option<Carriage>> {
let mut uses = vec![(false, false); program.files.len()];
let mut pending: Vec<&[Stmt]> = program.paragraphs.iter().map(|p| p.statements.as_slice()).collect();
while let Some(stmts) = pending.pop() {
for s in stmts {
pending.extend(crate::oo::bodies(s));
if let Stmt::Write { record, advancing: Some(a), .. } = s
&& let Some(k) = file_of(layout, record)
{
uses[k] = (true, uses[k].1 || a.before());
}
}
}
program
.files
.iter()
.zip(uses)
.map(|(f, (advancing, before))| {
let print = f.organization == Organization::Sequential && !f.sort && (advancing || f.linage.is_some() || !f.reports.is_empty());
print.then_some(Carriage { machine: before, reserved: reserves_first_byte(f, adv) })
})
.collect()
}
pub(crate) fn reserves_first_byte(f: &FileDecl, adv: bool) -> bool {
!adv && f.linage.is_none() && f.organization == Organization::Sequential && !f.sort
}
fn file_of(layout: &Layout, record: &Ref) -> Option<usize> {
match layout.resolve(&record.name, &record.qualifiers, record.pos) {
Ok(Resolved::Item(i)) => layout.items[i].file.map(usize::from),
_ => None,
}
}
pub(crate) fn check_write(program: &Program, layout: &Layout, record: &Ref, advancing: &Advancing, pos: syntax::Pos, errors: &mut Vec<Error>) {
if let Advancing::Mnemonic { name, environment, .. } = advancing
&& mnemonic_space(environment).is_none()
{
errors.push(syntax::messages::IWR0013.at(pos, format!("ADVANCING {name}: stacker selection ({environment}) on a card punch is not supported yet")));
}
let Some(f) = file_of(layout, record).map(|k| &program.files[k]) else { return };
match f.organization {
Organization::Indexed | Organization::Relative => errors.push(syntax::messages::IWC0144.at(pos, format!("WRITE ... ADVANCING: {} is not a sequential file", f.name))),
Organization::LineSequential if advancing.before() || matches!(advancing, Advancing::Mnemonic { .. }) => {
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)));
}
_ => {}
}
}