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, CallPlan, Collating, DebugId, Frame, FrameKind, MovePlan, Op, Place, PlaceId, Program, ReturnPoint, Returns, StorePlan, SymId, UpDown};
29use crate::oo::Running;
30use crate::picture::Sym;
31use crate::sql::Ran;
32use crate::storage::Val;
33use crate::store::{LaxRedefinition, ProgramFacts};
34use crate::unit::{Loader, RunUnit};
35use crate::vocab::{Figurative, Pos};
36use numeric::Options;
37use std::collections::{BTreeMap, HashMap};
38use std::rc::Rc;
39use zarch::ebcdic::{self, CodePage, Collation};
40
41pub use cics::run_task;
42pub(crate) use flow::Arrival;
43
44type R<T> = Result<T, Stop>;
45
46#[derive(Clone, Debug, PartialEq, Eq)]
48pub enum Halt {
49 Abend(Abend),
50 Unimplemented(String),
52}
53
54impl From<Abend> for Halt {
55 fn from(a: Abend) -> Self {
56 Self::Abend(a)
57 }
58}
59
60#[derive(Debug)]
62struct Stop(Box<Halt>);
63
64impl Stop {
65 fn halt(self) -> Halt {
66 *self.0
67 }
68}
69
70impl From<Halt> for Stop {
71 fn from(halt: Halt) -> Self {
72 Self(Box::new(halt))
73 }
74}
75
76impl From<Abend> for Stop {
77 fn from(a: Abend) -> Self {
78 Self(Box::new(Halt::Abend(a)))
79 }
80}
81
82impl From<Stop> for Halt {
83 fn from(stop: Stop) -> Self {
84 stop.halt()
85 }
86}
87
88fn not_yet(what: impl Into<String>) -> Stop {
89 Halt::Unimplemented(what.into()).into()
90}
91
92pub struct Code {
97 lowered: Result<Lowered, String>,
98 entries: Vec<String>,
99 files: usize,
100 size: usize,
101 nested: Vec<String>,
102 method: Option<String>,
103 facts: numeric::governs::Facts,
104}
105
106struct Lowered {
111 program: Program,
112 collation: Collation,
113 ordinals: Vec<u8>,
114 high_value: u8,
115 low_value: u8,
116 entry_of: Vec<Option<u32>>,
117 receivers: HashMap<PlaceId, SymId>,
118 variables: HashMap<PlaceId, Option<String>>,
119 pure: Vec<bool>,
120 quick: Vec<Option<place::Quick>>,
121 direct: Vec<bool>,
123 numbers: Vec<bool>,
125 literals: Vec<Option<(i64, numeric::precision::Places)>>,
127}
128
129impl Code {
130 pub fn new(program: Result<Program, String>, entries: Vec<String>, files: usize, size: usize, nested: Vec<String>, method: Option<String>, facts: numeric::governs::Facts) -> Self {
131 Self { lowered: program.map(Lowered::new), entries, files, size, nested, method, facts }
132 }
133
134 pub fn facts(&self) -> numeric::governs::Facts {
135 self.facts
136 }
137
138 pub fn program(&self) -> Option<&Program> {
139 self.lowered.as_ref().ok().map(|l| &l.program)
140 }
141
142 pub fn entry(&self, name: &str) -> Option<usize> {
144 self.entries.iter().position(|e| e == name)
145 }
146
147 pub fn shape(&self) -> (usize, usize) {
148 (self.files, self.size)
149 }
150
151 pub fn nested(&self) -> &[String] {
152 &self.nested
153 }
154}
155
156impl Lowered {
157 fn new(program: Program) -> Self {
158 let (collation, ordinals, high_value, low_value) = match &program.options.collating {
159 Collating::Native => (Collation::Native, (0..=255).collect(), ebcdic::HIGH_VALUE, ebcdic::LOW_VALUE),
160 Collating::Sequence(s) => {
161 let weights = Box::new(std::array::from_fn(|b| u16::from(s.positions[b])));
162 (Collation::Weights(weights), s.characters.clone(), s.high_value, s.low_value)
163 }
164 };
165 let mut entry_of = vec![None; program.blocks.len()];
166 for (i, paragraph) in program.paragraphs.iter().enumerate() {
167 if let Some(slot) = entry_of.get_mut(paragraph.entry as usize) {
168 *slot = Some(i as u32);
169 }
170 }
171 let receivers = receivers(&program);
172 let variables = if program.options.options.numcheck.is_some() { cond::conditional_variables(&program) } else { HashMap::new() };
173 let pure = place::pure_places(&program);
174 let quick = place::quick_places(&program);
175 let literals = program.consts.iter().map(value::const_number).collect();
176 let numbers = program.places.iter().map(place::number_item).collect();
177 let direct = program.places.iter().map(place::direct).collect();
178 Self { program, collation, ordinals, high_value, low_value, entry_of, receivers, variables, pure, quick, direct, numbers, literals }
179 }
180}
181
182fn receivers(p: &Program) -> HashMap<PlaceId, SymId> {
185 let mut names = HashMap::new();
186 let mut store = |place: PlaceId, plan: &StorePlan| {
187 if let StorePlan::Binary { name, .. } = plan {
188 names.entry(place).or_insert(*name);
189 }
190 };
191 let moved = |plan: &MovePlan| match plan {
192 MovePlan::Numeric { store, .. } => Some(*store),
193 _ => None,
194 };
195 for a in &p.plans.arith {
196 a.steps.iter().for_each(|s| store(s.target, &s.store));
197 a.remainder.iter().for_each(|r| store(r.target, &r.store));
198 }
199 for Block { ops, .. } in &p.blocks {
200 for op in ops {
201 match op {
202 Op::Move { to, plan, .. } | Op::Set { to, plan, .. } | Op::Accept { target: to, plan, .. } => moved(plan).iter().for_each(|s| store(*to, s)),
203 Op::Step { var, plan, .. } => store(*var, &plan.store),
204 Op::SetUpDown { targets, .. } => targets.iter().for_each(|(place, how)| {
205 if let UpDown::Number(plan) = how {
206 store(*place, &plan.store);
207 }
208 }),
209 _ => {}
210 }
211 }
212 }
213 for s in &p.plans.string {
214 s.pointer.iter().for_each(|(place, plan)| store(*place, plan));
215 }
216 for u in &p.plans.unstring {
217 u.pointer.iter().for_each(|(place, plan)| store(*place, plan));
218 u.tallying.iter().for_each(|(place, plan)| store(*place, &plan.store));
219 for into in &u.into {
220 moved(&into.plan).iter().for_each(|s| store(into.target, s));
221 into.count.iter().for_each(|(place, plan)| store(*place, plan));
222 }
223 }
224 for i in &p.plans.inspect {
225 i.tallying.iter().filter_map(|t| t.counter.as_ref()).for_each(|(place, plan)| store(*place, &plan.store));
226 }
227 for s in &p.plans.search_all {
228 store(s.index, &s.store);
229 }
230 names
231}
232
233pub fn run<L: Loader<Rc<Code>>>(code: &Code, me: usize, unit: &mut RunUnit<'_, Rc<Code>, L>, arguments: &[Option<usize>], main: bool) -> Result<Ending, Halt> {
237 let lowered = code.lowered.as_ref().map_err(|why| not_yet(format!("a program that does not lower ({why})")))?;
238 let mut vm = Vm::activation(lowered, me, unit, main)?;
239 for (&record, &address) in lowered.program.storage.using.iter().zip(arguments) {
240 vm.linkage[usize::from(record)] = address;
241 }
242 Ok(vm.run_from(None)?)
243}
244
245#[derive(Clone, Copy)]
248struct Facts<'p> {
249 code: &'p Lowered,
250}
251
252impl ProgramFacts for Facts<'_> {
253 fn options(&self) -> Options {
254 self.code.program.options.options
255 }
256
257 fn page(&self) -> &'static CodePage {
258 self.code.program.options.options.code_page()
259 }
260
261 fn figurative(&self, f: Figurative) -> u8 {
262 match f {
263 Figurative::HighValue => self.code.high_value,
264 Figurative::LowValue => self.code.low_value,
265 Figurative::Space => ebcdic::SPACE,
266 Figurative::Zero => ebcdic::ZERO,
267 Figurative::Quote => self.options().quote.byte(),
268 Figurative::Null => 0,
269 }
270 }
271
272 fn collation(&self) -> &Collation {
273 &self.code.collation
274 }
275
276 fn ordinal(&self, byte: u8) -> u16 {
277 self.code.collation.weight(byte) + 1
278 }
279
280 fn character(&self, ordinal: i64) -> Option<u8> {
281 usize::try_from(ordinal).ok().and_then(|n| n.checked_sub(1)).and_then(|i| self.code.ordinals.get(i)).copied()
282 }
283
284 fn characters(&self) -> usize {
285 self.code.ordinals.len()
286 }
287
288 fn decimal_point(&self) -> char {
289 if self.code.program.options.decimal_point_comma { ',' } else { '.' }
290 }
291
292 fn edit(&self, edit: u32) -> (&[Sym], &str) {
293 let e = &self.code.program.edits[edit as usize];
294 (&e.syms, &e.currency)
295 }
296
297 fn scaling(&self, item: usize) -> u32 {
298 self.code.program.places.get(item).map_or(0, |p| p.scaling)
299 }
300
301 fn item_name(&self, item: usize) -> String {
303 let p = &self.code.program;
304 let name = match (u32::try_from(item).ok().and_then(|i| self.code.receivers.get(&i)), p.places.get(item)) {
305 (Some(&name), _) => name,
306 (None, Some(Place { base: Base::Xml(_), .. }) | None) => return "RETURN-CODE".into(),
307 (None, Some(place)) => match self.code.variables.get(&(item as u32)) {
308 Some(Some(name)) => return name.clone(),
309 _ => place.name,
310 },
311 };
312 p.symbols[name as usize].clone()
313 }
314
315 fn lax_redefinition(&self, item: usize) -> Option<LaxRedefinition> {
316 self.code.program.places.get(item).and_then(|p| p.numcheck.lax)
317 }
318
319 fn numcheck_removed(&self, item: usize, _pos: Pos) -> bool {
320 self.code.program.places.get(item).is_some_and(|p| p.numcheck.removed)
321 }
322}
323
324struct Vm<'p, 'u, 'w, L: Loader<Rc<Code>>> {
327 code: &'p Lowered,
328 p: &'p Program,
329 me: usize,
330 base: usize,
331 local_base: usize,
332 linkage: Vec<Option<usize>>,
333 main: bool,
335 returns: Returns,
336 segment: u8,
337 line: u32,
339 arrival: Arrival,
340 debugging: bool,
342 locating: u32,
344 pending: Option<String>,
346 markup: markup::State,
348 io: files::State,
349 cics_handlers: Handlers,
352 serial: u64,
354 first: Option<&'p Lowered>,
356 whenever: Option<Ran>,
358 method: Option<Running>,
360 containers: Vec<scope::Container<'p>>,
362 memo: Option<place::Memo>,
363 called: Option<(&'p CallPlan, usize, Option<usize>)>,
366 spare: Spare,
368 unit: &'u mut RunUnit<'w, Rc<Code>, L>,
369}
370
371fn check_storage(code: &Lowered) -> R<()> {
373 let storage = &code.program.storage;
374 if !storage.local_image.is_empty() && (storage.init_abend.is_some() || !storage.init_reports.is_empty()) {
375 return Err(not_yet("VALUE initialization that reports or abends in a program with LOCAL-STORAGE"));
376 }
377 Ok(())
378}
379
380#[derive(Default)]
383struct Spare {
384 linkage: Vec<Option<usize>>,
385 armed: Vec<Option<ReturnPoint>>,
386 frames: Vec<Frame>,
387 using: Vec<Option<usize>>,
388 addresses: Vec<Option<usize>>,
389 lengths: Vec<usize>,
390 args: Vec<Val>,
391}
392
393impl<'p, 'u, 'w, L: Loader<Rc<Code>>> Vm<'p, 'u, 'w, L> {
394 fn activation(code: &'p Lowered, me: usize, unit: &'u mut RunUnit<'w, Rc<Code>, L>, main: bool) -> R<Self> {
397 Self::activation_within(code, me, unit, main, Vec::new(), Spare::default())
398 }
399
400 fn activation_within(code: &'p Lowered, me: usize, unit: &'u mut RunUnit<'w, Rc<Code>, L>, main: bool, containers: Vec<scope::Container<'p>>, spare: Spare) -> R<Self> {
403 check_storage(code)?;
404 let (base, fresh) = unit.activate(me, code.program.initial);
405 let mut vm = Self::over_reusing(code, me, base, unit, main, containers, spare);
406 vm.start_storage(fresh)?;
407 Ok(vm)
408 }
409
410 fn start_storage(&mut self, fresh: bool) -> R<()> {
413 let storage = &self.p.storage;
414 self.bind_shared()?;
415 if !storage.local_image.is_empty() {
416 self.local_base = self.unit.push_temporary(&storage.local_image);
417 self.unit.mark_input(self.local_base, storage.local_image.len(), false);
418 }
419 if fresh {
420 let base = self.base;
421 self.unit.mem[base..base + storage.image.len()].copy_from_slice(&storage.image);
422 self.unit.mark_input(base, storage.image.len(), false);
423 for &report in &storage.init_reports {
424 let _ = writeln!(self.unit.err, "{}", self.sym(report));
425 }
426 if let Some(abend) = storage.init_abend {
427 return Err(self.abend(abend, None).into());
428 }
429 self.unit.initialized(self.me);
430 }
431 Ok(())
432 }
433
434 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 {
436 Self::over_reusing(code, me, base, unit, main, containers, Spare::default())
437 }
438
439 fn over_reusing(code: &'p Lowered, me: usize, base: usize, unit: &'u mut RunUnit<'w, Rc<Code>, L>, main: bool, containers: Vec<scope::Container<'p>>, spare: Spare) -> Self {
440 let p = &code.program;
441 let serial = unit.cics.as_mut().map_or(0, crate::cics::Task::next_activation);
442 let first = unit.programs[me].compiled.is_none().then_some(code);
443 let main_frame = Frame { id: 0, kind: FrameKind::Main, displaced: None, segment: 0, depth: unit.depth as u32, temps: Vec::new() };
444 let Spare { mut linkage, mut armed, mut frames, .. } = spare;
445 linkage.clear();
446 linkage.resize(p.storage.linkage.len(), None);
447 armed.clear();
448 armed.resize(p.paragraphs.len(), None);
449 frames.clear();
450 frames.push(main_frame);
451 Self {
452 code,
453 p,
454 me,
455 base,
456 local_base: 0,
457 linkage,
458 main,
459 returns: Returns { armed, saved: BTreeMap::new(), frames, next_frame: 1 },
460 segment: 0,
461 line: 0,
462 arrival: Arrival::Start,
463 debugging: false,
464 locating: 0,
465 pending: None,
466 markup: markup::State::default(),
467 io: files::State::default(),
468 cics_handlers: Handlers::default(),
469 serial,
470 first,
471 whenever: None,
472 method: None,
473 containers,
474 memo: None,
475 called: None,
476 spare: Spare::default(),
477 unit,
478 }
479 }
480
481 fn facts(&self) -> Facts<'p> {
482 Facts { code: self.code }
483 }
484
485 fn sym(&self, id: SymId) -> &'p str {
486 &self.p.symbols[id as usize]
487 }
488
489 fn pos(&self, at: DebugId) -> Pos {
490 self.p.debug.positions[at as usize]
491 }
492
493 fn abend(&self, id: AbendId, at: Option<DebugId>) -> Abend {
495 let text = &self.p.abends[id as usize];
496 let pos = text.at.or(at).map(|d| self.pos(d)).unwrap_or_default();
497 Abend { code: text.code.clone(), message: self.sym(text.message).to_owned(), pos, file: None }
498 }
499
500 fn lift<T>(&mut self, result: R<T>, pos: Pos) -> Result<T, Abend> {
503 result.map_err(|stop| match stop.halt() {
504 Halt::Abend(a) => a,
505 Halt::Unimplemented(what) => {
506 self.pending.get_or_insert(what);
507 Abend::ironwork("the VM stopped inside a library call", pos)
508 }
509 })
510 }
511
512 fn settle<T>(&mut self, result: Result<T, Abend>) -> R<T> {
514 match (result, self.pending.take()) {
515 (_, Some(what)) => Err(Halt::Unimplemented(what).into()),
516 (result, None) => result.map_err(Stop::from),
517 }
518 }
519}
520