use super::*;
use crate::declaratives::{DEBUG_CONTENTS, DEBUG_LINE, DEBUG_NAME, Span, debug_name, mode_index};
use syntax::report::ReportStmt;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub(super) enum Arrival {
#[default]
Perform,
Start,
GoTo,
FallThrough,
Use,
Sort(&'static str),
}
impl Arrival {
fn contents(self) -> &'static str {
match self {
Self::Perform => "PERFORM LOOP",
Self::Start => "START PROGRAM",
Self::GoTo => "",
Self::FallThrough => "FALL THROUGH",
Self::Use => "USE PROCEDURE",
Self::Sort(procedure) => procedure,
}
}
}
#[derive(Default)]
pub(super) struct State {
pub(super) arrival: Arrival,
pub(super) line: Pos,
pub(super) debugging: bool,
pub(super) leaving: Option<Flow>,
pub(super) failed: Option<usize>,
}
pub(super) fn statement_pos(s: &Stmt) -> Option<Pos> {
Some(match s {
Stmt::Move { pos, .. }
| Stmt::Compute { pos, .. }
| Stmt::If { pos, .. }
| Stmt::PerformInline { pos, .. }
| Stmt::PerformProc { pos, .. }
| Stmt::Evaluate { pos, .. }
| Stmt::Display { pos, .. }
| Stmt::Open { pos, .. }
| Stmt::Close { pos, .. }
| Stmt::Write { pos, .. }
| Stmt::Rewrite { pos, .. }
| Stmt::Delete { pos, .. }
| Stmt::Start { pos, .. }
| Stmt::Initialize { pos, .. }
| Stmt::GoTo { pos, .. }
| Stmt::GoToDepending { pos, .. }
| Stmt::Alter { pos, .. }
| Stmt::Entry { pos, .. }
| Stmt::Goback { pos }
| Stmt::ExitProgram { pos }
| Stmt::Cancel { pos, .. }
| Stmt::Set { pos, .. }
| Stmt::Accept { pos, .. }
| Stmt::ExitMethod { pos }
| Stmt::StopRun { pos } => *pos,
Stmt::Arith(a) => a.pos,
Stmt::Read(r) => r.pos,
Stmt::Call(c) => c.pos,
Stmt::String(st) => st.pos,
Stmt::Unstring(u) => u.pos,
Stmt::Inspect(i) => i.pos,
Stmt::Search(se) => se.pos,
Stmt::Exec(block) => block.pos,
Stmt::Invoke(i) => i.pos,
Stmt::Report(r) => match &**r {
ReportStmt::Initiate { pos, .. } | ReportStmt::Generate { pos, .. } | ReportStmt::Terminate { pos, .. } | ReportStmt::Suppress { pos } => *pos,
},
Stmt::Sorting(so) => match &**so {
Sorting::Sort(st) => st.pos,
Sorting::Release { pos, .. } | Sorting::Return { pos, .. } => *pos,
},
Stmt::NextSentence | Stmt::SentenceEnd | Stmt::Continue | Stmt::Exit { .. } => return None,
})
}
impl<'p> Machine<'p, '_, '_> {
pub(super) fn error_declarative(&self, k: usize, mode: Option<OpenMode>) -> Option<Span> {
let table = self.declaratives;
table.files.get(k).copied().flatten().or_else(|| mode.and_then(|m| table.modes[mode_index(m)]))
}
pub(super) fn run_error_declarative(&mut self, (first, last): Span, pos: Pos) -> R<()> {
self.nest(pos)?;
self.uses.arrival = Arrival::Use;
let flow = self.run_paragraphs(first, last);
self.unit.depth -= 1;
match flow? {
leaving @ (Flow::GoTo(_) | Flow::End(_) | Flow::Resume(..) | Flow::Return(_)) => {
self.uses.leaving = Some(leaving);
Err(Abend { code: AbendCode::Signal(Signal::DeclarativeExit), message: String::new(), pos })
}
_ => Ok(()),
}
}
pub(super) fn declarative_exit(&mut self) -> Flow {
self.uses.leaving.take().unwrap_or(Flow::Next)
}
pub(super) fn debug_before(&mut self, i: usize, arrival: Arrival) -> R<Option<Flow>> {
let table = self.declaratives;
let Some(Some((section, name))) = table.triggers.get(i) else { return Ok(None) };
let pos = self.program.paragraphs[i].pos;
let line = if arrival == Arrival::Start { pos } else { self.uses.line };
self.run_debugging(*section, name, line, arrival.contents(), pos)
}
pub(super) fn debug_alter(&mut self, pairs: &[(ProcName, ProcName)], pos: Pos) -> R<Option<Flow>> {
let table = self.declaratives;
if table.triggers.is_empty() || table.declarative_alters.contains(&pos) {
return Ok(None);
}
for (paragraph, target) in pairs {
let at = self.procedure(paragraph, pos)?.0;
if let Some(Some((section, name))) = table.triggers.get(at)
&& let Some(flow) = self.run_debugging(*section, name, pos, &debug_name(target), pos)?
{
return Ok(Some(flow));
}
}
Ok(None)
}
fn run_debugging(&mut self, (first, last): Span, name: &str, line: Pos, contents: &str, pos: Pos) -> R<Option<Flow>> {
if self.uses.debugging {
return Ok(None);
}
if let Some(item) = self.declaratives.debug_item {
let (at, size) = (self.base + self.layout.items[item].offset as usize, self.layout.items[item].size as usize);
self.unit.mem[at..at + size].fill(ebcdic::SPACE);
for ((offset, len), text) in [(DEBUG_LINE, &format!("{:06}", line.line)[..]), (DEBUG_NAME, name), (DEBUG_CONTENTS, contents)] {
let bytes = self.page.encode(text).map_err(|e| Abend::ironwork(e.to_string(), pos))?;
let n = bytes.len().min(len);
self.unit.mem[at + offset..at + offset + n].copy_from_slice(&bytes[..n]);
}
}
let saved = self.uses.line;
self.uses.debugging = true;
let flow = self.nest(pos).and_then(|()| {
let flow = self.run_paragraphs(first, last);
self.unit.depth -= 1;
flow
});
self.uses.debugging = false;
self.uses.line = saved;
Ok(match flow? {
leaving @ (Flow::GoTo(_) | Flow::End(_) | Flow::Resume(..) | Flow::Return(_)) => Some(leaving),
_ => None,
})
}
}