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, 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}
104
105struct Lowered {
110 program: Program,
111 collation: Collation,
112 ordinals: Vec<u8>,
113 high_value: u8,
114 low_value: u8,
115 entry_of: Vec<Option<u32>>,
116 receivers: HashMap<PlaceId, SymId>,
117 variables: HashMap<PlaceId, Option<String>>,
118 pure: Vec<bool>,
119}
120
121impl Code {
122 pub fn new(program: Result<Program, String>, entries: Vec<String>, files: usize, size: usize, nested: Vec<String>, method: Option<String>) -> Self {
123 Self { lowered: program.map(Lowered::new), entries, files, size, nested, method }
124 }
125
126 pub fn program(&self) -> Option<&Program> {
127 self.lowered.as_ref().ok().map(|l| &l.program)
128 }
129
130 pub fn entry(&self, name: &str) -> Option<usize> {
132 self.entries.iter().position(|e| e == name)
133 }
134
135 pub fn shape(&self) -> (usize, usize) {
136 (self.files, self.size)
137 }
138
139 pub fn nested(&self) -> &[String] {
140 &self.nested
141 }
142}
143
144impl Lowered {
145 fn new(program: Program) -> Self {
146 let (collation, ordinals, high_value, low_value) = match &program.options.collating {
147 Collating::Native => (Collation::Native, (0..=255).collect(), ebcdic::HIGH_VALUE, ebcdic::LOW_VALUE),
148 Collating::Sequence(s) => {
149 let weights = Box::new(std::array::from_fn(|b| u16::from(s.positions[b])));
150 (Collation::Weights(weights), s.characters.clone(), s.high_value, s.low_value)
151 }
152 };
153 let mut entry_of = vec![None; program.blocks.len()];
154 for (i, paragraph) in program.paragraphs.iter().enumerate() {
155 if let Some(slot) = entry_of.get_mut(paragraph.entry as usize) {
156 *slot = Some(i as u32);
157 }
158 }
159 let receivers = receivers(&program);
160 let variables = if program.options.options.numcheck.is_some() { cond::conditional_variables(&program) } else { HashMap::new() };
161 let pure = place::pure_places(&program);
162 Self { program, collation, ordinals, high_value, low_value, entry_of, receivers, variables, pure }
163 }
164}
165
166fn receivers(p: &Program) -> HashMap<PlaceId, SymId> {
169 let mut names = HashMap::new();
170 let mut store = |place: PlaceId, plan: &StorePlan| {
171 if let StorePlan::Binary { name, .. } = plan {
172 names.entry(place).or_insert(*name);
173 }
174 };
175 let moved = |plan: &MovePlan| match plan {
176 MovePlan::Numeric { store, .. } => Some(*store),
177 _ => None,
178 };
179 for a in &p.plans.arith {
180 a.steps.iter().for_each(|s| store(s.target, &s.store));
181 a.remainder.iter().for_each(|r| store(r.target, &r.store));
182 }
183 for Block { ops, .. } in &p.blocks {
184 for op in ops {
185 match op {
186 Op::Move { to, plan, .. } | Op::Set { to, plan, .. } | Op::Accept { target: to, plan, .. } => moved(plan).iter().for_each(|s| store(*to, s)),
187 Op::Step { var, plan, .. } => store(*var, &plan.store),
188 Op::SetUpDown { targets, .. } => targets.iter().for_each(|(place, how)| {
189 if let UpDown::Number(plan) = how {
190 store(*place, &plan.store);
191 }
192 }),
193 _ => {}
194 }
195 }
196 }
197 for s in &p.plans.string {
198 s.pointer.iter().for_each(|(place, plan)| store(*place, plan));
199 }
200 for u in &p.plans.unstring {
201 u.pointer.iter().for_each(|(place, plan)| store(*place, plan));
202 u.tallying.iter().for_each(|(place, plan)| store(*place, &plan.store));
203 for into in &u.into {
204 moved(&into.plan).iter().for_each(|s| store(into.target, s));
205 into.count.iter().for_each(|(place, plan)| store(*place, plan));
206 }
207 }
208 for i in &p.plans.inspect {
209 i.tallying.iter().filter_map(|t| t.counter.as_ref()).for_each(|(place, plan)| store(*place, &plan.store));
210 }
211 for s in &p.plans.search_all {
212 store(s.index, &s.store);
213 }
214 names
215}
216
217pub fn run<L: Loader<Rc<Code>>>(code: &Code, me: usize, unit: &mut RunUnit<'_, Rc<Code>, L>, arguments: &[Option<usize>], main: bool) -> Result<Ending, Halt> {
221 let lowered = code.lowered.as_ref().map_err(|why| not_yet(format!("a program that does not lower ({why})")))?;
222 let mut vm = Vm::activation(lowered, me, unit, main)?;
223 for (&record, &address) in lowered.program.storage.using.iter().zip(arguments) {
224 vm.linkage[usize::from(record)] = address;
225 }
226 Ok(vm.run_from(None)?)
227}
228
229#[derive(Clone, Copy)]
232struct Facts<'p> {
233 code: &'p Lowered,
234}
235
236impl ProgramFacts for Facts<'_> {
237 fn options(&self) -> Options {
238 self.code.program.options.options
239 }
240
241 fn page(&self) -> &'static CodePage {
242 self.code.program.options.options.code_page()
243 }
244
245 fn figurative(&self, f: Figurative) -> u8 {
246 match f {
247 Figurative::HighValue => self.code.high_value,
248 Figurative::LowValue => self.code.low_value,
249 Figurative::Space => ebcdic::SPACE,
250 Figurative::Zero => ebcdic::ZERO,
251 Figurative::Quote => self.options().quote.byte(),
252 Figurative::Null => 0,
253 }
254 }
255
256 fn collation(&self) -> &Collation {
257 &self.code.collation
258 }
259
260 fn ordinal(&self, byte: u8) -> u16 {
261 self.code.collation.weight(byte) + 1
262 }
263
264 fn character(&self, ordinal: i64) -> Option<u8> {
265 usize::try_from(ordinal).ok().and_then(|n| n.checked_sub(1)).and_then(|i| self.code.ordinals.get(i)).copied()
266 }
267
268 fn characters(&self) -> usize {
269 self.code.ordinals.len()
270 }
271
272 fn decimal_point(&self) -> char {
273 if self.code.program.options.decimal_point_comma { ',' } else { '.' }
274 }
275
276 fn edit(&self, edit: u32) -> (&[Sym], &str) {
277 let e = &self.code.program.edits[edit as usize];
278 (&e.syms, &e.currency)
279 }
280
281 fn scaling(&self, item: usize) -> u32 {
282 self.code.program.places.get(item).map_or(0, |p| p.scaling)
283 }
284
285 fn item_name(&self, item: usize) -> String {
287 let p = &self.code.program;
288 let name = match (u32::try_from(item).ok().and_then(|i| self.code.receivers.get(&i)), p.places.get(item)) {
289 (Some(&name), _) => name,
290 (None, Some(Place { base: Base::Xml(_), .. }) | None) => return "RETURN-CODE".into(),
291 (None, Some(place)) => match self.code.variables.get(&(item as u32)) {
292 Some(Some(name)) => return name.clone(),
293 _ => place.name,
294 },
295 };
296 p.symbols[name as usize].clone()
297 }
298
299 fn lax_redefinition(&self, item: usize) -> Option<LaxRedefinition> {
300 self.code.program.places.get(item).and_then(|p| p.numcheck.lax)
301 }
302
303 fn numcheck_removed(&self, item: usize, _pos: Pos) -> bool {
304 self.code.program.places.get(item).is_some_and(|p| p.numcheck.removed)
305 }
306}
307
308struct Vm<'p, 'u, 'w, L: Loader<Rc<Code>>> {
311 code: &'p Lowered,
312 p: &'p Program,
313 me: usize,
314 base: usize,
315 local_base: usize,
316 linkage: Vec<Option<usize>>,
317 main: bool,
319 returns: Returns,
320 segment: u8,
321 line: u32,
323 arrival: Arrival,
324 debugging: bool,
326 locating: u32,
328 pending: Option<String>,
330 markup: markup::State,
332 io: files::State,
333 cics_handlers: Handlers,
336 serial: u64,
338 first: Option<&'p Lowered>,
340 whenever: Option<Ran>,
342 method: Option<Running>,
344 containers: Vec<scope::Container<'p>>,
346 memo: Option<place::Memo>,
347 spare: Spare,
349 unit: &'u mut RunUnit<'w, Rc<Code>, L>,
350}
351
352#[derive(Default)]
355struct Spare {
356 linkage: Vec<Option<usize>>,
357 armed: Vec<Option<ReturnPoint>>,
358 frames: Vec<Frame>,
359 using: Vec<Option<usize>>,
360 addresses: Vec<Option<usize>>,
361 args: Vec<Val>,
362}
363
364impl<'p, 'u, 'w, L: Loader<Rc<Code>>> Vm<'p, 'u, 'w, L> {
365 fn activation(code: &'p Lowered, me: usize, unit: &'u mut RunUnit<'w, Rc<Code>, L>, main: bool) -> R<Self> {
368 Self::activation_within(code, me, unit, main, Vec::new(), Spare::default())
369 }
370
371 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> {
374 let p = &code.program;
375 let storage = &p.storage;
376 if !storage.local_image.is_empty() && (storage.init_abend.is_some() || !storage.init_reports.is_empty()) {
377 return Err(not_yet("VALUE initialization that reports or abends in a program with LOCAL-STORAGE"));
378 }
379 let (base, fresh) = unit.activate(me, p.initial);
380 let mut vm = Self::over_reusing(code, me, base, unit, main, containers, spare);
381 vm.bind_shared()?;
382 if !storage.local_image.is_empty() {
383 vm.local_base = vm.unit.push_temporary(&storage.local_image);
384 vm.unit.mark_input(vm.local_base, storage.local_image.len(), false);
385 }
386 if fresh {
387 vm.unit.mem[base..base + storage.image.len()].copy_from_slice(&storage.image);
388 vm.unit.mark_input(base, storage.image.len(), false);
389 for &report in &storage.init_reports {
390 let _ = writeln!(vm.unit.err, "{}", vm.sym(report));
391 }
392 if let Some(abend) = storage.init_abend {
393 return Err(vm.abend(abend, None).into());
394 }
395 vm.unit.initialized(me);
396 }
397 Ok(vm)
398 }
399
400 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 {
402 Self::over_reusing(code, me, base, unit, main, containers, Spare::default())
403 }
404
405 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 {
406 let p = &code.program;
407 let serial = unit.cics.as_mut().map_or(0, crate::cics::Task::next_activation);
408 let first = unit.programs[me].compiled.is_none().then_some(code);
409 let main_frame = Frame { id: 0, kind: FrameKind::Main, displaced: None, segment: 0, depth: unit.depth as u32, temps: Vec::new() };
410 let Spare { mut linkage, mut armed, mut frames, .. } = spare;
411 linkage.clear();
412 linkage.resize(p.storage.linkage.len(), None);
413 armed.clear();
414 armed.resize(p.paragraphs.len(), None);
415 frames.clear();
416 frames.push(main_frame);
417 Self {
418 code,
419 p,
420 me,
421 base,
422 local_base: 0,
423 linkage,
424 main,
425 returns: Returns { armed, saved: BTreeMap::new(), frames, next_frame: 1 },
426 segment: 0,
427 line: 0,
428 arrival: Arrival::Start,
429 debugging: false,
430 locating: 0,
431 pending: None,
432 markup: markup::State::default(),
433 io: files::State::default(),
434 cics_handlers: Handlers::default(),
435 serial,
436 first,
437 whenever: None,
438 method: None,
439 containers,
440 memo: None,
441 spare: Spare::default(),
442 unit,
443 }
444 }
445
446 fn facts(&self) -> Facts<'p> {
447 Facts { code: self.code }
448 }
449
450 fn sym(&self, id: SymId) -> &'p str {
451 &self.p.symbols[id as usize]
452 }
453
454 fn pos(&self, at: DebugId) -> Pos {
455 self.p.debug.positions[at as usize]
456 }
457
458 fn abend(&self, id: AbendId, at: Option<DebugId>) -> Abend {
460 let text = &self.p.abends[id as usize];
461 let pos = text.at.or(at).map(|d| self.pos(d)).unwrap_or_default();
462 Abend { code: text.code.clone(), message: self.sym(text.message).to_owned(), pos, file: None }
463 }
464
465 fn lift<T>(&mut self, result: R<T>, pos: Pos) -> Result<T, Abend> {
468 result.map_err(|stop| match stop.halt() {
469 Halt::Abend(a) => a,
470 Halt::Unimplemented(what) => {
471 self.pending.get_or_insert(what);
472 Abend::ironwork("the VM stopped inside a library call", pos)
473 }
474 })
475 }
476
477 fn settle<T>(&mut self, result: Result<T, Abend>) -> R<T> {
479 match (result, self.pending.take()) {
480 (_, Some(what)) => Err(Halt::Unimplemented(what).into()),
481 (result, None) => result.map_err(Stop::from),
482 }
483 }
484}