1mod arith;
10mod call;
11mod cics;
12mod cond;
13mod files;
14mod flow;
15mod function;
16mod markup;
17mod oo;
18mod ops;
19mod place;
20mod report;
21mod scope;
22mod sort;
23mod sql;
24mod value;
25
26use crate::abend::{Abend, Ending};
27use crate::cics::Handlers;
28use crate::lir::{AbendId, Base, Block, Collating, DebugId, Frame, FrameKind, MovePlan, Op, Place, PlaceId, Program, Returns, StorePlan, SymId, UpDown};
29use crate::oo::Running;
30use crate::picture::Sym;
31use crate::sql::Ran;
32use crate::store::{LaxRedefinition, ProgramFacts};
33use crate::unit::{Loader, RunUnit};
34use crate::vocab::{Figurative, Pos};
35use numeric::Options;
36use std::collections::{BTreeMap, HashMap};
37use std::rc::Rc;
38use zarch::ebcdic::{self, CodePage, Collation};
39
40pub use cics::run_task;
41pub(crate) use flow::Arrival;
42
43type R<T> = Result<T, Halt>;
44
45#[derive(Clone, Debug, PartialEq, Eq)]
47pub enum Halt {
48 Abend(Abend),
49 Unimplemented(String),
51}
52
53impl From<Abend> for Halt {
54 fn from(a: Abend) -> Self {
55 Self::Abend(a)
56 }
57}
58
59fn not_yet(what: impl Into<String>) -> Halt {
60 Halt::Unimplemented(what.into())
61}
62
63pub struct Code {
68 lowered: Result<Lowered, String>,
69 entries: Vec<String>,
70 files: usize,
71 size: usize,
72 nested: Vec<String>,
73 method: Option<String>,
74}
75
76struct Lowered {
81 program: Program,
82 collation: Collation,
83 ordinals: Vec<u8>,
84 high_value: u8,
85 low_value: u8,
86 entry_of: Vec<Option<u32>>,
87 receivers: HashMap<PlaceId, SymId>,
88 variables: HashMap<PlaceId, Option<String>>,
89}
90
91impl Code {
92 pub fn new(program: Result<Program, String>, entries: Vec<String>, files: usize, size: usize, nested: Vec<String>, method: Option<String>) -> Self {
93 Self { lowered: program.map(Lowered::new), entries, files, size, nested, method }
94 }
95
96 pub fn program(&self) -> Option<&Program> {
97 self.lowered.as_ref().ok().map(|l| &l.program)
98 }
99
100 pub fn entry(&self, name: &str) -> Option<usize> {
102 self.entries.iter().position(|e| e == name)
103 }
104
105 pub fn shape(&self) -> (usize, usize) {
106 (self.files, self.size)
107 }
108
109 pub fn nested(&self) -> &[String] {
110 &self.nested
111 }
112}
113
114impl Lowered {
115 fn new(program: Program) -> Self {
116 let (collation, ordinals, high_value, low_value) = match &program.options.collating {
117 Collating::Native => (Collation::Native, (0..=255).collect(), ebcdic::HIGH_VALUE, ebcdic::LOW_VALUE),
118 Collating::Sequence(s) => {
119 let weights = Box::new(std::array::from_fn(|b| u16::from(s.positions[b])));
120 (Collation::Weights(weights), s.characters.clone(), s.high_value, s.low_value)
121 }
122 };
123 let mut entry_of = vec![None; program.blocks.len()];
124 for (i, paragraph) in program.paragraphs.iter().enumerate() {
125 if let Some(slot) = entry_of.get_mut(paragraph.entry as usize) {
126 *slot = Some(i as u32);
127 }
128 }
129 let receivers = receivers(&program);
130 let variables = if program.options.options.numcheck.is_some() { cond::conditional_variables(&program) } else { HashMap::new() };
131 Self { program, collation, ordinals, high_value, low_value, entry_of, receivers, variables }
132 }
133}
134
135fn receivers(p: &Program) -> HashMap<PlaceId, SymId> {
138 let mut names = HashMap::new();
139 let mut store = |place: PlaceId, plan: &StorePlan| {
140 if let StorePlan::Binary { name, .. } = plan {
141 names.entry(place).or_insert(*name);
142 }
143 };
144 let moved = |plan: &MovePlan| match plan {
145 MovePlan::Numeric { store, .. } => Some(*store),
146 _ => None,
147 };
148 for a in &p.plans.arith {
149 a.steps.iter().for_each(|s| store(s.target, &s.store));
150 a.remainder.iter().for_each(|r| store(r.target, &r.store));
151 }
152 for Block { ops, .. } in &p.blocks {
153 for op in ops {
154 match op {
155 Op::Move { to, plan, .. } | Op::Set { to, plan, .. } | Op::Accept { target: to, plan, .. } => moved(plan).iter().for_each(|s| store(*to, s)),
156 Op::Step { var, plan, .. } => store(*var, &plan.store),
157 Op::SetUpDown { targets, .. } => targets.iter().for_each(|(place, how)| {
158 if let UpDown::Number(plan) = how {
159 store(*place, &plan.store);
160 }
161 }),
162 _ => {}
163 }
164 }
165 }
166 for s in &p.plans.string {
167 s.pointer.iter().for_each(|(place, plan)| store(*place, plan));
168 }
169 for u in &p.plans.unstring {
170 u.pointer.iter().for_each(|(place, plan)| store(*place, plan));
171 u.tallying.iter().for_each(|(place, plan)| store(*place, &plan.store));
172 for into in &u.into {
173 moved(&into.plan).iter().for_each(|s| store(into.target, s));
174 into.count.iter().for_each(|(place, plan)| store(*place, plan));
175 }
176 }
177 for i in &p.plans.inspect {
178 i.tallying.iter().filter_map(|t| t.counter.as_ref()).for_each(|(place, plan)| store(*place, &plan.store));
179 }
180 for s in &p.plans.search_all {
181 store(s.index, &s.store);
182 }
183 names
184}
185
186pub fn run<L: Loader<Rc<Code>>>(code: &Code, me: usize, unit: &mut RunUnit<'_, Rc<Code>, L>, arguments: &[Option<usize>]) -> Result<Ending, Halt> {
189 let lowered = code.lowered.as_ref().map_err(|why| not_yet(format!("a program that does not lower ({why})")))?;
190 let mut vm = Vm::activation(lowered, me, unit, true)?;
191 for (&record, &address) in lowered.program.storage.using.iter().zip(arguments) {
192 vm.linkage[usize::from(record)] = address;
193 }
194 vm.run_from(None)
195}
196
197#[derive(Clone, Copy)]
200struct Facts<'p> {
201 code: &'p Lowered,
202}
203
204impl ProgramFacts for Facts<'_> {
205 fn options(&self) -> Options {
206 self.code.program.options.options
207 }
208
209 fn page(&self) -> &'static CodePage {
210 self.code.program.options.options.code_page()
211 }
212
213 fn figurative(&self, f: Figurative) -> u8 {
214 match f {
215 Figurative::HighValue => self.code.high_value,
216 Figurative::LowValue => self.code.low_value,
217 Figurative::Space => ebcdic::SPACE,
218 Figurative::Zero => ebcdic::ZERO,
219 Figurative::Quote => self.options().quote.byte(),
220 Figurative::Null => 0,
221 }
222 }
223
224 fn collation(&self) -> &Collation {
225 &self.code.collation
226 }
227
228 fn ordinal(&self, byte: u8) -> u16 {
229 self.code.collation.weight(byte) + 1
230 }
231
232 fn character(&self, ordinal: i64) -> Option<u8> {
233 usize::try_from(ordinal).ok().and_then(|n| n.checked_sub(1)).and_then(|i| self.code.ordinals.get(i)).copied()
234 }
235
236 fn characters(&self) -> usize {
237 self.code.ordinals.len()
238 }
239
240 fn decimal_point(&self) -> char {
241 if self.code.program.options.decimal_point_comma { ',' } else { '.' }
242 }
243
244 fn edit(&self, edit: u32) -> (&[Sym], &str) {
245 let e = &self.code.program.edits[edit as usize];
246 (&e.syms, &e.currency)
247 }
248
249 fn scaling(&self, item: usize) -> u32 {
250 self.code.program.places.get(item).map_or(0, |p| p.scaling)
251 }
252
253 fn item_name(&self, item: usize) -> String {
255 let p = &self.code.program;
256 let name = match (u32::try_from(item).ok().and_then(|i| self.code.receivers.get(&i)), p.places.get(item)) {
257 (Some(&name), _) => name,
258 (None, Some(Place { base: Base::Xml(_), .. }) | None) => return "RETURN-CODE".into(),
259 (None, Some(place)) => match self.code.variables.get(&(item as u32)) {
260 Some(Some(name)) => return name.clone(),
261 _ => place.name,
262 },
263 };
264 p.symbols[name as usize].clone()
265 }
266
267 fn lax_redefinition(&self, item: usize) -> Option<LaxRedefinition> {
268 self.code.program.places.get(item).and_then(|p| p.numcheck.lax)
269 }
270
271 fn numcheck_removed(&self, item: usize, _pos: Pos) -> bool {
272 self.code.program.places.get(item).is_some_and(|p| p.numcheck.removed)
273 }
274}
275
276struct Vm<'p, 'u, 'w, L: Loader<Rc<Code>>> {
279 code: &'p Lowered,
280 p: &'p Program,
281 me: usize,
282 base: usize,
283 local_base: usize,
284 linkage: Vec<Option<usize>>,
285 main: bool,
287 returns: Returns,
288 segment: u8,
289 line: u32,
291 arrival: Arrival,
292 debugging: bool,
294 locating: u32,
296 pending: Option<String>,
298 markup: markup::State,
300 io: files::State,
301 cics_handlers: Handlers,
304 serial: u64,
306 first: Option<&'p Lowered>,
308 whenever: Option<Ran>,
310 method: Option<Running>,
312 containers: Vec<scope::Container<'p>>,
314 unit: &'u mut RunUnit<'w, Rc<Code>, L>,
315}
316
317impl<'p, 'u, 'w, L: Loader<Rc<Code>>> Vm<'p, 'u, 'w, L> {
318 fn activation(code: &'p Lowered, me: usize, unit: &'u mut RunUnit<'w, Rc<Code>, L>, main: bool) -> R<Self> {
321 Self::activation_within(code, me, unit, main, Vec::new())
322 }
323
324 fn activation_within(code: &'p Lowered, me: usize, unit: &'u mut RunUnit<'w, Rc<Code>, L>, main: bool, containers: Vec<scope::Container<'p>>) -> R<Self> {
326 let p = &code.program;
327 let storage = &p.storage;
328 if !storage.local_image.is_empty() && (storage.init_abend.is_some() || !storage.init_reports.is_empty()) {
329 return Err(not_yet("VALUE initialization that reports or abends in a program with LOCAL-STORAGE"));
330 }
331 let (base, fresh) = unit.activate(me, p.initial);
332 let mut vm = Self::over(code, me, base, unit, main, containers);
333 vm.bind_shared()?;
334 if !storage.local_image.is_empty() {
335 vm.local_base = vm.unit.push_temporary(&storage.local_image);
336 vm.unit.mark_input(vm.local_base, storage.local_image.len(), false);
337 }
338 if fresh {
339 vm.unit.mem[base..base + storage.image.len()].copy_from_slice(&storage.image);
340 vm.unit.mark_input(base, storage.image.len(), false);
341 for &report in &storage.init_reports {
342 let _ = writeln!(vm.unit.err, "{}", vm.sym(report));
343 }
344 if let Some(abend) = storage.init_abend {
345 return Err(vm.abend(abend, None).into());
346 }
347 vm.unit.initialized(me);
348 }
349 Ok(vm)
350 }
351
352 fn over(code: &'p Lowered, me: usize, base: usize, unit: &'u mut RunUnit<'w, Rc<Code>, L>, main: bool, containers: Vec<scope::Container<'p>>) -> Self {
354 let p = &code.program;
355 let serial = unit.cics.as_mut().map_or(0, crate::cics::Task::next_activation);
356 let first = unit.programs[me].compiled.is_none().then_some(code);
357 let main_frame = Frame { id: 0, kind: FrameKind::Main, displaced: None, segment: 0, depth: unit.depth as u32, temps: Vec::new() };
358 Self {
359 code,
360 p,
361 me,
362 base,
363 local_base: 0,
364 linkage: vec![None; p.storage.linkage.len()],
365 main,
366 returns: Returns { armed: vec![None; p.paragraphs.len()], saved: BTreeMap::new(), frames: vec![main_frame], next_frame: 1 },
367 segment: 0,
368 line: 0,
369 arrival: Arrival::Start,
370 debugging: false,
371 locating: 0,
372 pending: None,
373 markup: markup::State::default(),
374 io: files::State::default(),
375 cics_handlers: Handlers::default(),
376 serial,
377 first,
378 whenever: None,
379 method: None,
380 containers,
381 unit,
382 }
383 }
384
385 fn facts(&self) -> Facts<'p> {
386 Facts { code: self.code }
387 }
388
389 fn sym(&self, id: SymId) -> &'p str {
390 &self.p.symbols[id as usize]
391 }
392
393 fn pos(&self, at: DebugId) -> Pos {
394 self.p.debug.positions[at as usize]
395 }
396
397 fn abend(&self, id: AbendId, at: Option<DebugId>) -> Abend {
399 let text = &self.p.abends[id as usize];
400 let pos = text.at.or(at).map(|d| self.pos(d)).unwrap_or_default();
401 Abend { code: text.code.clone(), message: self.sym(text.message).to_owned(), pos, file: None }
402 }
403
404 fn lift<T>(&mut self, result: R<T>, pos: Pos) -> Result<T, Abend> {
407 result.map_err(|halt| match halt {
408 Halt::Abend(a) => a,
409 Halt::Unimplemented(what) => {
410 self.pending.get_or_insert(what);
411 Abend::ironwork("the VM stopped inside a library call", pos)
412 }
413 })
414 }
415
416 fn settle<T>(&mut self, result: Result<T, Abend>) -> R<T> {
418 match (result, self.pending.take()) {
419 (_, Some(what)) => Err(Halt::Unimplemented(what)),
420 (result, None) => result.map_err(Halt::Abend),
421 }
422 }
423}