1pub use rt::abend::{Abend, Ending};
6use crate::abend::{AbendCode, Signal};
7use crate::layout::{Kind, Layout, Resolved};
8use rt::storage::{Loc, Val};
9pub(crate) use rt::storage::literal_fixed;
10use crate::unit::{ADDRESS_BASE, Event, LoadError, OS_COMMAND_ROUTINES, RETURN_CODE, RunUnit};
11use crate::Compiled;
12use compile::arith::{decimal_exponent, divided_exponent, function_dmax};
13use compile::statements::{flatten_and, key_term, numval_currency, upon_console};
14use compile::values::value_kind;
15use numeric::precision::{Dmax, Fixed, Places};
16use numeric::{LeServices, Options, ProgramScope, Switched};
17use rt::fixed::{align, places_of};
18use rt::arith;
19use rt::callee::{self, Bindings, By, Callee};
20use rt::display::utf16_text;
21use rt::host::Host;
22use rt::lir::{ByteClass, CallArg, ConvertTable, Converting, SignTest, StringSource, TrimSide};
23use rt::loc;
24use rt::store;
25use rt::text::UnstringField;
26use std::cmp::Ordering;
27use std::collections::HashMap;
28use syntax::Pos;
29use syntax::ast::*;
30use zarch::ebcdic::{self, CodePage};
31use zarch::hfp::{Hfp, Precision};
32
33mod cics;
34pub(crate) mod cics_bind;
35mod declaratives;
36mod facts;
37mod file_io;
38mod function;
39mod intrinsic;
40use intrinsic::Within;
41mod json;
42mod le_services;
43mod oo;
44mod parmcheck;
45mod perform;
46mod report;
47mod scope;
48mod sort;
49pub(crate) mod sql;
50mod xml;
51
52type R<T> = Result<T, Abend>;
53
54enum Flow {
55 Next,
56 End(Ending),
57 GoTo(usize),
58 ExitParagraph,
59 ExitSection,
60 ExitPerform,
61 ExitPerformCycle,
62 NextSentence,
63 Resume(usize, usize),
65 Return(u64),
67}
68
69pub struct Machine<'p, 'u, 'w> {
70 compiled: &'p Compiled,
71 program: &'p Program,
72 layout: &'p Layout,
73 options: Options,
74 ssrange: bool,
75 page: &'static CodePage,
76 collating: &'p crate::collating::Sequence,
77 when_compiled: rt::lir::CompileTime,
78 resolved: HashMap<(String, Vec<String>), Resolved>,
79 me: usize,
81 base: usize,
82 linkage: Vec<Option<usize>>,
84 local_base: usize,
86 main: bool,
88 cics_handlers: cics::Handlers,
91 serial: u64,
93 first: Option<&'p Compiled>,
96 report_writer: &'p crate::report::Writer,
97 carriage: &'p [Option<crate::printer::Carriage>],
99 oo: oo::Frame,
101 sort: Option<sort::Active>,
103 segment: u8,
105 declaratives: &'p crate::declaratives::Table,
106 uses: declaratives::State,
107 returns: perform::Returns,
108 xml: xml::Registers,
110 containers: Vec<scope::Frame<'p>>,
112 functions: &'p [compile::function::Udf],
114 unit: &'u mut RunUnit<'w>,
115}
116
117enum Step {
118 Again,
119 Leave,
120 Out(Flow),
121}
122
123impl<'p, 'u, 'w> Machine<'p, 'u, 'w> {
124 pub fn activation(compiled: &'p Compiled, me: usize, unit: &'u mut RunUnit<'w>, main: bool) -> R<Self> {
127 Self::activation_within(compiled, me, unit, main, Vec::new())
128 }
129
130 fn activation_within(compiled: &'p Compiled, me: usize, unit: &'u mut RunUnit<'w>, main: bool, containers: Vec<scope::Frame<'p>>) -> R<Self> {
132 let (base, fresh) = unit.activate(me, compiled.program.initial);
133 let mut m = Self::over(compiled, me, base, unit, main);
134 m.containers = containers;
135 m.bind_shared()?;
136 m.initial_values(fresh)
137 }
138
139 fn initial_values(mut self, fresh: bool) -> R<Self> {
140 let (local, size) = (self.layout.local_size as usize, self.layout.size as usize);
141 if local > 0 {
142 self.local_base = self.unit.push_temporary(&vec![0; local]);
143 self.initialize_values(true)?;
144 self.unit.mark_input(self.local_base, local, false);
145 }
146 if fresh {
147 self.unit.mem[self.base..self.base + size].fill(0);
148 self.initialize_values(false)?;
149 self.unit.mark_input(self.base, size, false);
150 self.unit.initialized(self.me);
151 }
152 Ok(self)
153 }
154
155 fn over(compiled: &'p Compiled, me: usize, base: usize, unit: &'u mut RunUnit<'w>, main: bool) -> Self {
157 let serial = unit.cics.as_mut().map_or(0, crate::cics::Task::next_activation);
158 let first = unit.programs[me].compiled.is_none().then_some(compiled);
159 Self {
160 compiled,
161 program: &compiled.program,
162 layout: &compiled.layout,
163 options: compiled.options,
164 ssrange: compiled.ssrange,
165 page: compiled.options.code_page(),
166 collating: &compiled.collating,
167 when_compiled: compiled.when_compiled,
168 resolved: HashMap::new(),
169 me,
170 base,
171 linkage: vec![None; compiled.layout.linkage_roots.len()],
172 local_base: 0,
173 main,
174 cics_handlers: cics::Handlers::default(),
175 serial,
176 first,
177 report_writer: &compiled.report_writer,
178 carriage: &compiled.carriage,
179 oo: oo::Frame::default(),
180 sort: None,
181 segment: 0,
182 declaratives: &compiled.declaratives,
183 uses: declaratives::State::default(),
184 returns: perform::Returns::new(compiled.program.paragraphs.len()),
185 xml: xml::Registers::default(),
186 containers: Vec::new(),
187 functions: &compiled.functions,
188 unit,
189 }
190 }
191
192 fn initialize_values(&mut self, local: bool) -> R<()> {
194 let base = if local { self.local_base } else { self.base };
195 compile::values::initialize(self.compiled, self.unit, base, local)
196 }
197
198 pub fn run_procedure(&mut self) -> R<Ending> {
199 self.run_from(None)
200 }
201
202 fn enter_segment(&mut self, priority: u8) {
205 if priority == self.segment {
206 return;
207 }
208 self.segment = priority;
209 if priority >= 50 {
210 let program = self.program;
211 for (i, target) in self.unit.programs[self.me].altered.iter_mut().enumerate() {
212 if program.paragraphs[i].priority == priority {
213 *target = None;
214 }
215 }
216 }
217 }
218
219 fn run_sentences(&mut self, stmts: &'p [Stmt]) -> R<Flow> {
221 let mut i = 0;
222 while i < stmts.len() {
223 match self.exec(&stmts[i])? {
224 Flow::Next => i += 1,
225 Flow::NextSentence => i = stmts[i..].iter().position(|s| *s == Stmt::SentenceEnd).map_or(stmts.len(), |j| i + j + 1),
226 other => return Ok(other),
227 }
228 }
229 Ok(Flow::Next)
230 }
231
232 fn run_block(&mut self, stmts: &'p [Stmt]) -> R<Flow> {
233 for s in stmts {
234 match self.exec(s)? {
235 Flow::Next => {}
236 other => return Ok(other),
237 }
238 }
239 Ok(Flow::Next)
240 }
241
242 fn procedure(&self, p: &ProcName, pos: Pos) -> R<(usize, usize)> {
243 crate::procedure(self.program, p).map_err(|m| Abend::ironwork(m, pos))
244 }
245
246 fn exec(&mut self, s: &'p Stmt) -> R<Flow> {
248 if !self.declaratives.triggers.is_empty()
249 && let Some(pos) = declaratives::statement_pos(s)
250 {
251 self.uses.line = pos;
252 }
253 if self.unit.limited()
254 && let Some(pos) = declaratives::statement_pos(s)
255 {
256 self.unit.start_statement(self.me, pos)?;
257 }
258 if (self.unit.statements.is_some() || self.unit.taint.is_some())
259 && let Some(pos) = declaratives::statement_pos(s)
260 {
261 self.unit.statement_starts();
262 if self.unit.traces(pos.line) {
263 let file = self.event_file(pos);
264 self.unit.notify(Event::Statement { file: &file, line: pos.line });
265 }
266 }
267 match self.statement(s) {
268 Err(Abend { code: AbendCode::Signal(Signal::DeclarativeExit), .. }) => Ok(self.declarative_exit()),
269 Err(Abend { code: AbendCode::Signal(Signal::StopRun), .. }) => Ok(Flow::End(Ending::StopRun)),
270 flow => flow,
271 }
272 }
273
274 fn statement(&mut self, s: &'p Stmt) -> R<Flow> {
275 match s {
276 Stmt::Move { from, to, pos } => {
277 for r in to {
278 let dest = self.locate_written(|m| m.locate_receiving(r))?;
279 let (val, src) = self.move_source(from, dest, *pos)?;
280 self.assign(dest, val, src, *pos)?;
281 }
282 }
283 Stmt::Compute { targets, expr, size_error, pos } => {
284 let computations: Vec<(Target, Expr)> = targets.iter().map(|t| (t.clone(), expr.clone())).collect();
285 return self.arithmetic(&computations, None, size_error.as_ref(), false, *pos);
286 }
287 Stmt::Arith(a) => return self.arithmetic(&a.computations, a.remainder.as_ref(), a.size_error.as_ref(), true, a.pos),
288 Stmt::Corresponding(c) => return Err(Abend::ironwork("CORRESPONDING reached the interpreter unexpanded", c.pos)),
289 Stmt::If { cond, then, otherwise, pos } => {
290 let branch = if self.condition(cond, *pos)? { then } else { otherwise };
291 return self.run_block(branch);
292 }
293 Stmt::Evaluate { subjects, whens, other, pos } => {
294 for w in whens {
295 for alternative in &w.alternatives {
296 if self.alternative_matches(subjects, alternative, *pos)? {
297 return self.run_block(&w.body);
298 }
299 }
300 }
301 return self.run_block(other);
302 }
303 Stmt::PerformProc { from, thru, repeat, pos } => {
304 let (start, first_end) = self.procedure(from, *pos)?;
305 let end = match thru {
306 Some(t) => self.procedure(t, *pos)?.1,
307 None => first_end,
308 };
309 let statement = if matches!(repeat, Loop::Once) { self.after(s) } else { None };
310 return self.repeat(repeat, *pos, &mut |m: &mut Self| {
311 m.uses.line = *pos;
312 m.perform_range(start, end, None, statement)
313 });
314 }
315 Stmt::PerformInline { body, repeat, pos } => return self.repeat(repeat, *pos, &mut |m: &mut Self| m.run_block(body)),
316 Stmt::Display { items, upon: Some(upon), pos, .. } if matches!(upon.device.as_str(), "ENVIRONMENT-NAME" | "ENVIRONMENT-VALUE") => {
317 let text = self.display_text(items, false, *pos)?;
318 if upon.device == "ENVIRONMENT-VALUE" { self.unit.environment.set(&text) } else { self.unit.environment.name(&text) }
319 }
320 Stmt::Display { items, upon: Some(upon), pos, .. } if upon.device == "ARGUMENT-NUMBER" => {
321 let n = match items.as_slice() {
322 [item] => self.integer(&Expr::Operand(item.clone()), *pos)?,
323 _ => return Err(Abend::ironwork("DISPLAY UPON ARGUMENT-NUMBER shows one item", *pos)),
324 };
325 self.unit.arguments.position(n);
326 }
327 Stmt::Display { items, screen: Some(screen), pos, .. } => {
328 let text = self.display_text(items, false, *pos)?;
329 let at = self.screen_at(screen, *pos)?;
330 rt::crt::display(self.unit, at, &text, clearing(screen));
331 }
332 Stmt::Display { items, upon: Some(upon), no_advancing, pos, .. } if upon.device == "SYSERR" => {
333 let text = self.display_text(items, false, *pos)?;
334 if self.unit.observed() {
335 self.sink("log", *pos, &text);
336 }
337 rt::display::write(&mut *self.unit.err, &text, *no_advancing, *pos)?;
338 }
339 Stmt::Display { items, upon, no_advancing, pos, .. } => self.display(items, upon_console(upon.as_ref()), *no_advancing, *pos)?,
340 Stmt::Open { files, pos } => {
341 for (mode, name) in files {
342 self.open_file(*mode, name, *pos)?;
343 }
344 }
345 Stmt::Close { files, pos } => {
346 for (name, closing) in files {
347 self.close_file_with(name, *closing, *pos)?;
348 }
349 }
350 Stmt::Read(r) => return self.read_stmt(r),
351 Stmt::Write { record, from, advancing, invalid, end_of_page, pos } => return self.write_stmt(record, from.as_ref(), advancing.as_ref(), invalid, end_of_page, *pos),
352 Stmt::Rewrite { record, from, invalid, pos } => return self.rewrite_stmt(record, from.as_ref(), invalid, *pos),
353 Stmt::Delete { file, invalid, pos } => return self.delete_stmt(file, invalid, *pos),
354 Stmt::DeleteFile { files, pos } => self.delete_files(files, *pos)?,
355 Stmt::Start { file, key, invalid, pos } => return self.start_stmt(file, key.as_ref(), invalid, *pos),
356 Stmt::Initialize { targets, with, pos } => {
357 let with = with.as_deref().unwrap_or(&NO_PHRASES);
358 for r in targets {
359 let loc = self.locate_written(|m| m.locate(r))?;
360 let item = (loc.item != usize::MAX).then_some(loc.item);
361 let category = match (item, &r.refmod) {
362 (_, Some(_)) => self.layout.refmod_category(item, loc.kind),
363 (Some(item), None) => {
364 self.initialize(item, loc.offset, with, *pos)?;
365 continue;
366 }
367 (None, None) => DataCategory::Numeric,
368 };
369 match with.initial_value(Some(category), false) {
370 Some(InitialValue::Replacing(by)) => {
371 let (val, src) = self.operand_with_loc(by, *pos)?;
372 self.assign(loc, val, src, *pos)?;
373 }
374 Some(_) if r.refmod.is_some() => self.assign(loc, Val::Fig(Figurative::Space), None, *pos)?,
375 Some(_) => self.unit.write(loc.offset, &vec![0; loc.len]),
376 None => {}
377 }
378 }
379 }
380 Stmt::GoTo { target: Some(target), pos } => return Ok(Flow::GoTo(self.procedure(target, *pos)?.0)),
381 Stmt::GoTo { target: None, .. } | Stmt::Entry { .. } => {}
382 Stmt::GoToDepending { targets, on, pos } => {
383 let n = self.integer(&Expr::Operand(Operand::Ref(on.clone())), *pos)?;
384 if let Some(target) = usize::try_from(n).ok().and_then(|n| targets.get(n.wrapping_sub(1))) {
385 return Ok(Flow::GoTo(self.procedure(target, *pos)?.0));
386 }
387 }
388 Stmt::Alter { pairs, pos } => {
389 for (paragraph, target) in pairs {
390 let (at, to) = (self.procedure(paragraph, *pos)?.0, self.procedure(target, *pos)?.0);
391 let paragraphs = self.program.paragraphs.len();
392 let altered = &mut self.unit.programs[self.me].altered;
393 altered.resize(paragraphs, None);
394 altered[at] = Some(to);
395 }
396 if let Some(flow) = self.debug_alter(pairs, *pos)? {
397 return Ok(flow);
398 }
399 }
400 Stmt::Goback { .. } => return Ok(Flow::End(Ending::Goback)),
401 Stmt::ExitProgram { .. } if self.main => {}
402 Stmt::ExitProgram { .. } => return Ok(Flow::End(Ending::Goback)),
403 Stmt::Call(c) => return self.call(c),
404 Stmt::Cancel { targets, pos } => {
405 for t in targets {
406 let name = self.program_name(t, *pos)?;
407 callee::cancel(self.unit, &name, *pos)?;
408 }
409 }
410 Stmt::Set { set, pos } => self.set(set, *pos)?,
411 Stmt::Accept { target, exception, screen: Some(screen), pos, .. } => {
412 let inputs = match screen.screen {
413 Some(_) => screen
414 .inputs
415 .iter()
416 .map(|i| {
417 let at = Some((i.line as usize, i.column as usize));
418 Ok(rt::crt::Input { target: self.locate(&i.target)?, field: self.locate(&i.field)?, at, update: i.update, secure: i.secure })
419 })
420 .collect::<R<Vec<_>>>()?,
421 None => {
422 let loc = self.locate(target)?;
423 let at = self.screen_at(screen, *pos)?;
424 vec![rt::crt::Input { target: loc, field: loc, at, update: screen.update, secure: screen.secure }]
425 }
426 };
427 let raised = rt::crt::accept(&self.facts(), self.unit, &inputs, *pos)?;
428 return self.overflow_branch(raised, &exception.on, &exception.not_on);
429 }
430 Stmt::Accept { target, from: from @ (AcceptFrom::ArgumentValue | AcceptFrom::EnvironmentValue), exception, pos, .. } => {
431 let raised = self.accept(target, *from, *pos)?;
432 return self.overflow_branch(raised, &exception.on, &exception.not_on);
433 }
434 Stmt::Accept { target, from, pos, .. } => {
435 self.accept(target, *from, *pos)?;
436 }
437 Stmt::String(st) => return self.string_stmt(st),
438 Stmt::Unstring(u) => return self.unstring(u),
439 Stmt::Inspect(i) => self.inspect(i)?,
440 Stmt::Search(se) => return self.search(se),
441 Stmt::Sorting(s) => return self.sorting(s),
442 Stmt::NextSentence => return Ok(Flow::NextSentence),
443 Stmt::Exec(block) if block.declarative() => {}
444 Stmt::Exec(block) if block.kind == ExecKind::Cics => return self.cics(block),
445 Stmt::Exec(block) if block.kind == ExecKind::Sql => return self.sql(block),
446 Stmt::Report(r) => return self.report_statement(r),
447 Stmt::Exec(block) => {
448 let kind = match block.kind {
449 ExecKind::Sql => "SQL",
450 ExecKind::Cics => "CICS",
451 ExecKind::Dli => "DLI",
452 ExecKind::Other => "",
453 };
454 return Err(rt::refusal::IWR0060.ending(AbendCode::Exec, format_args!("EXEC {kind} {} was reached: ironwork for COBOL checks EXEC statements but does not run them yet", block.command), block.pos));
455 }
456 Stmt::Invoke(i) => return self.invoke(i),
457 Stmt::JsonGenerate(g) => return self.json_generate(g),
458 Stmt::XmlParse(x) => return self.xml_parse(x),
459 Stmt::XmlGenerate(x) => return self.xml_generate(x),
460 Stmt::JsonParse(j) => return self.json_parse(j),
461 Stmt::ExitMethod { .. } => return Ok(Flow::End(Ending::Goback)),
462 Stmt::SentenceEnd => {}
463 Stmt::StopRun { .. } => return Ok(Flow::End(Ending::StopRun)),
464 Stmt::Exit { kind: ExitKind::Paragraph, .. } => return Ok(Flow::ExitParagraph),
465 Stmt::Exit { kind: ExitKind::Section, .. } => return Ok(Flow::ExitSection),
466 Stmt::Exit { kind: ExitKind::Perform, .. } => return Ok(Flow::ExitPerform),
467 Stmt::Exit { kind: ExitKind::PerformCycle, .. } => return Ok(Flow::ExitPerformCycle),
468 Stmt::Continue { .. } | Stmt::Exit { kind: ExitKind::Plain, .. } => {}
469 }
470 Ok(Flow::Next)
471 }
472
473 fn alternative_matches(&mut self, subjects: &[Subject], objects: &[Object], pos: Pos) -> R<bool> {
474 for (subject, object) in subjects.iter().zip(objects) {
475 let hit = match (subject, object) {
476 (_, Object::Any) => true,
477 (Subject::Bool(b), Object::Bool(o)) => b == o,
478 (Subject::Bool(b), Object::Cond(c)) => self.condition(c, pos)? == *b,
479 (Subject::Cond(c), Object::Bool(o)) => self.condition(c, pos)? == *o,
480 (Subject::Cond(c), Object::Cond(d)) => self.condition(c, pos)? == self.condition(d, pos)?,
481 (Subject::Expr(e), Object::Value { not, from, thru }) => {
482 let inside = match thru {
483 None => self.compare(e, from, pos)? == Ordering::Equal,
484 Some(t) => self.compare(e, from, pos)? != Ordering::Less && self.compare(e, t, pos)? != Ordering::Greater,
485 };
486 inside != *not
487 }
488 _ => return Err(Abend::ironwork("a WHEN object of a different kind from its subject", pos)),
489 };
490 if !hit {
491 return Ok(false);
492 }
493 }
494 Ok(true)
495 }
496
497 fn repeat(&mut self, repeat: &'p Loop, pos: Pos, body: &mut dyn FnMut(&mut Self) -> R<Flow>) -> R<Flow> {
500 self.unit.enter(pos)?;
501 let flow = self.repeat_nested(repeat, pos, body);
502 self.unit.depth -= 1;
503 flow
504 }
505
506 fn repeat_nested(&mut self, repeat: &'p Loop, pos: Pos, body: &mut dyn FnMut(&mut Self) -> R<Flow>) -> R<Flow> {
507 let mut run = |m: &mut Self| -> R<Step> {
508 Ok(match body(m)? {
509 Flow::Next | Flow::ExitPerformCycle => Step::Again,
510 Flow::ExitPerform => Step::Leave,
511 other => Step::Out(other),
512 })
513 };
514 match repeat {
515 Loop::Once => match run(self)? {
516 Step::Out(f) => return Ok(f),
517 Step::Again | Step::Leave => {}
518 },
519 Loop::Times(count) => {
520 for _ in 0..self.integer(count, pos)?.max(0) {
521 match run(self)? {
522 Step::Again => {}
523 Step::Leave => break,
524 Step::Out(f) => return Ok(f),
525 }
526 }
527 }
528 Loop::Forever => loop {
529 match run(self)? {
530 Step::Again => {}
531 Step::Leave => break,
532 Step::Out(f) => return Ok(f),
533 }
534 },
535 Loop::Until { cond, test_after } => loop {
536 if !test_after && self.condition(cond, pos)? {
537 break;
538 }
539 match run(self)? {
540 Step::Again => {}
541 Step::Leave => break,
542 Step::Out(f) => return Ok(f),
543 }
544 if *test_after && self.condition(cond, pos)? {
545 break;
546 }
547 },
548 Loop::Varying { varying, after, test_after } => {
549 let levels: Vec<&'p Varying> = std::iter::once(&**varying).chain(after).collect();
550 let count = if *test_after { 1 } else { levels.len() };
551 for v in &levels[..count] {
552 self.vary_from(v, pos)?;
553 }
554 if let Step::Out(f) = self.vary(&levels, *test_after, pos, &mut run)? {
555 return Ok(f);
556 }
557 }
558 }
559 Ok(Flow::Next)
560 }
561
562 fn vary(&mut self, levels: &[&'p Varying], test_after: bool, pos: Pos, run: &mut dyn FnMut(&mut Self) -> R<Step>) -> R<Step> {
566 let Some((level, inner)) = levels.split_first() else { return run(self) };
567 loop {
568 if !test_after && self.condition(&level.until, pos)? {
569 return Ok(Step::Again);
570 }
571 if test_after && let Some(next) = inner.first() {
572 self.vary_from(next, pos)?;
573 }
574 match self.vary(inner, test_after, pos, run)? {
575 Step::Again => {}
576 other => return Ok(other),
577 }
578 if test_after && self.condition(&level.until, pos)? {
579 return Ok(Step::Again);
580 }
581 self.vary_by(level, pos)?;
582 if !test_after && let Some(next) = inner.first() {
583 self.vary_from(next, pos)?;
584 }
585 }
586 }
587
588 fn vary_from(&mut self, v: &Varying, pos: Pos) -> R<()> {
589 let var = self.locate_written(|m| m.locate(&v.var))?;
590 let start = self.expr_value(&v.from, pos)?;
591 self.assign(var, start, None, pos)
592 }
593
594 fn vary_by(&mut self, v: &Varying, pos: Pos) -> R<()> {
597 let var = self.locate(&v.var)?;
598 let step = Expr::Bin(Box::new(Expr::Operand(Operand::Ref(v.var.clone()))), BinOp::Add, Box::new(v.by.clone()));
599 if let Kind::Float(_) = var.kind {
600 return self.arithmetic(&[(Target { r: v.var.clone(), rounded: false }, step)], None, None, false, pos).map(|_| ());
601 }
602 let dmax = var.kind.digits_scale().map_or(0, |(_, s)| s).max(self.dmax(&step)?);
603 let next = self.eval_fixed(&step, dmax, pos)?;
604 store::store_fixed(&self.facts(), self.unit, var, &next, false, pos)
605 }
606
607 fn resolve(&mut self, r: &Ref) -> R<Resolved> {
608 let key = (r.name.clone(), r.qualifiers.clone());
609 if let Some(&hit) = self.resolved.get(&key) {
610 return Ok(hit);
611 }
612 let found = self.layout.resolve(&r.name, &r.qualifiers, r.pos).map_err(|e| Abend::ironwork(e.message, r.pos))?;
613 self.resolved.insert(key, found);
614 Ok(found)
615 }
616
617 fn locate(&mut self, r: &Ref) -> R<Loc> {
618 self.locate_as(r, false)
619 }
620
621 fn locate_written(&mut self, locate: impl FnOnce(&mut Self) -> R<Loc>) -> R<Loc> {
624 let was = self.unit.writing(true);
625 let loc = locate(self);
626 self.unit.writing(was);
627 loc
628 }
629
630 fn locate_receiving(&mut self, r: &Ref) -> R<Loc> {
634 self.locate_as(r, true)
635 }
636
637 fn locate_as(&mut self, r: &Ref, receiving: bool) -> R<Loc> {
638 if let Some(loc) = self.oo_register(r)?.or(self.xml_register(r)?) {
639 self.unit.taint_read(loc);
640 return Ok(loc);
641 }
642 if r.name == "RETURN-CODE" && r.qualifiers.is_empty() && !self.layout.items.iter().any(|i| i.name.as_deref() == Some("RETURN-CODE")) {
643 let loc = Loc { offset: RETURN_CODE, len: 2, kind: Kind::Binary { digits: 4, scale: 0, signed: true, native: numeric::Native::No }, item: usize::MAX };
644 self.unit.taint_read(loc);
645 return Ok(loc);
646 }
647 let Resolved::Item(index) = self.resolve(r)? else {
648 return Err(Abend::ironwork(format!("{} is a condition-name, not a data item", r.name), r.pos));
649 };
650 self.locate_item(index, r, receiving)
651 }
652
653 fn locate_item(&mut self, index: usize, r: &Ref, receiving: bool) -> R<Loc> {
657 let layout = self.layout;
658 let item = &layout.items[index];
659 if r.subscripts.len() != item.dims.len() {
660 return Err(Abend::ironwork(format!("{} takes {} subscripts, not {}", r.name, item.dims.len(), r.subscripts.len()), r.pos));
661 }
662 let base = match item.linkage {
663 Some(l) => loc::linkage_base(self.linkage[l as usize], &r.name, r.pos)?,
664 None if item.local => self.local_base,
665 None => self.base,
666 };
667 let mut offset = (base + item.offset as usize) as i64;
668 for &t in &item.moved_by {
669 offset -= self.unused(t, r.pos)?;
670 }
671 let mut composed = 0;
672 for (&(stride, _), sub) in item.dims.iter().zip(&r.subscripts) {
673 let s = self.integer(sub, r.pos)?;
674 composed += loc::subscript(s, stride);
675 }
676 if self.ssrange
677 && let Some((displacement, extent)) = layout.table_range(index)
678 {
679 loc::table_reference(i64::from(displacement) + composed, i64::from(item.size), i64::from(extent), &r.name, r.pos)?;
680 }
681 offset += composed;
682 let (mut len, mut kind) = (item.size as i64, item.kind);
683 if !item.odo.is_empty() && !(receiving && r.refmod.is_none() && !item.followed && self.objects_within(&item.odo, index)?) {
684 for &t in &item.odo {
685 len -= self.unused(t, r.pos)?;
686 }
687 }
688 if let Some(rm) = &r.refmod {
689 let start = self.integer(&rm.start, r.pos)?;
690 let length = match &rm.length {
691 Some(l) => Some(self.integer(l, r.pos)?),
692 None => None,
693 };
694 let unit = if matches!(kind, Kind::National | Kind::Dbcs { .. }) { 2 } else { 1 };
697 let (from, length) = loc::refmod(len / unit, start, length, self.ssrange, &r.name, r.pos)?;
698 offset += from * unit;
699 len = length * unit;
700 kind = match kind {
701 Kind::National => Kind::National,
702 Kind::Dbcs { .. } => Kind::Dbcs { justified: false, edit: None },
703 _ => Kind::Alnum { justified: false },
704 };
705 }
706 let (offset, len) = loc::within(offset, len, self.unit.mem.len(), &r.name, r.pos)?;
707 let loc = Loc { offset, len, kind, item: index };
708 self.unit.taint_read(loc);
709 Ok(loc)
710 }
711
712 fn objects_within(&mut self, tables: &[usize], group: usize) -> R<bool> {
714 for &t in tables {
715 if !self.object_within(t, group)? {
716 return Ok(false);
717 }
718 }
719 Ok(true)
720 }
721
722 fn unused(&mut self, t: usize, pos: Pos) -> R<i64> {
724 let table = &self.layout.items[t];
725 let (max, element) = (table.occurs, table.size);
726 let current = self.occurrences(t, pos)?;
727 Ok(loc::unused(max, current, element))
728 }
729
730 fn moved_within(&mut self, member: usize, holder: usize, pos: Pos) -> R<usize> {
733 let layout = self.layout;
734 let mut moved = 0;
735 for &t in layout.items[member].moved_by.iter().filter(|t| !layout.items[holder].moved_by.contains(t)) {
736 moved += self.unused(t, pos)? as usize;
737 }
738 Ok(moved)
739 }
740
741 fn object_within(&mut self, t: usize, group: usize) -> R<bool> {
743 let layout = self.layout;
744 let Some(object) = &layout.items[t].depending_on else { return Ok(false) };
745 let Resolved::Item(mut at) = self.resolve(object)? else { return Ok(false) };
746 loop {
747 if at == group {
748 return Ok(true);
749 }
750 match layout.items[at].parent {
751 Some(p) => at = p,
752 None => return Ok(false),
753 }
754 }
755 }
756
757 fn occurrences(&mut self, table: usize, pos: Pos) -> R<u32> {
760 let layout = self.layout;
761 let item = &layout.items[table];
762 let Some(object) = &item.depending_on else { return Ok(item.occurs) };
763 let count = self.integer(&Expr::Operand(Operand::Ref(object.clone())), pos)?;
764 loc::occurrences(count, item.occurs, self.ssrange, &object.name, pos)
765 }
766
767 fn integer(&mut self, e: &Expr, pos: Pos) -> R<i64> {
768 let dmax = self.dmax(e)?;
769 let v = self.eval_fixed(e, dmax, pos)?;
770 let whole = align(&v, 0, false).and_then(|m| m.to_u128()).and_then(|m| i64::try_from(m).ok());
771 let whole = whole.ok_or_else(|| Abend::ironwork("an integer operand beyond 64 bits", pos))?;
772 Ok(if v.negative { -whole } else { whole })
773 }
774
775 fn literal_value(&self, lit: &Literal, pos: Pos) -> R<Val> {
776 compile::values::literal_value(self.page, lit, pos)
777 }
778
779 fn decimal_point(&self) -> char {
781 if self.program.environment.decimal_point_comma { ',' } else { '.' }
782 }
783
784 fn default_currency(&self) -> String {
785 numval_currency(&self.program.environment.currency)
786 }
787
788 fn operand_with_loc(&mut self, op: &Operand, pos: Pos) -> R<(Val, Option<Loc>)> {
789 if let Operand::Ref(r) = op {
790 let loc = self.locate(r)?;
791 self.numcheck(loc, false, r.pos)?;
792 return Ok((store::read(&self.facts(), &self.unit.mem, loc, r.pos)?, Some(loc)));
793 }
794 Ok((self.operand(op, pos)?, None))
795 }
796
797 fn numcheck(&mut self, loc: Loc, as_integer: bool, pos: Pos) -> R<()> {
799 if self.options.numcheck.is_none() {
800 return Ok(());
801 }
802 let facts = self.facts();
803 rt::store::numcheck(&facts, self.unit, loc, as_integer, &self.program.id, pos)
804 }
805
806 fn move_source(&mut self, from: &Operand, dest: Loc, pos: Pos) -> R<(Val, Option<Loc>)> {
808 let Operand::Ref(r) = from else { return self.operand_with_loc(from, pos) };
809 let loc = self.locate(r)?;
810 let check = store::move_check(&self.options, loc.kind, dest.kind);
811 store::numcheck_sender(&self.facts(), self.unit, loc, check, &self.program.id, r.pos)?;
812 Ok((store::move_sender(&self.facts(), &self.unit.mem, loc, dest, r.pos)?, Some(loc)))
813 }
814
815 fn operand(&mut self, op: &Operand, pos: Pos) -> R<Val> {
816 match op {
817 Operand::Ref(r) => {
818 let loc = self.locate(r)?;
819 self.numcheck(loc, false, r.pos)?;
820 store::read(&self.facts(), &self.unit.mem, loc, r.pos)
821 }
822 Operand::Literal(lit) => self.literal_value(lit, pos),
823 Operand::LengthOf(r) => {
824 let layout = self.layout;
825 let fixed = match layout.resolve(&r.name, &r.qualifiers, r.pos) {
826 Ok(Resolved::Item(i)) if r.refmod.is_none() && layout.items[i].odo.is_empty() && layout.items[i].linkage.is_some() => Some(layout.items[i].size as usize),
827 _ => None,
828 };
829 let len = match fixed {
830 Some(len) => len,
831 None => self.locate(&layout.length_of_ref(r))?.len,
832 };
833 Ok(Val::Num(Fixed::new(len as i128, Places::new(9, 0))))
834 }
835 Operand::Function(f) => self.function(f, Within::Own),
836 Operand::AddressOf(r) => Ok(Val::Address(self.address_of(r)?)),
837 }
838 }
839
840 fn set_integer(&mut self, r: &Ref, value: i64, pos: Pos) -> R<()> {
841 let dest = self.locate(r)?;
842 store::set_integer(&self.facts(), self.unit, dest, value, pos)
843 }
844
845 fn overflow_branch(&mut self, overflow: bool, on: &'p Option<Vec<Stmt>>, not_on: &'p Option<Vec<Stmt>>) -> R<Flow> {
846 match (overflow, on, not_on) {
847 (true, Some(body), _) | (false, _, Some(body)) => self.run_block(body),
848 _ => Ok(Flow::Next),
849 }
850 }
851
852 fn string_stmt(&mut self, st: &'p StringStmt) -> R<Flow> {
853 let sources: Vec<_> = st.sources.iter().map(|(op, d)| StringSource { chars: facts::chars(op), delimiter: match d {
854 Delimiter::Size => None,
855 Delimiter::By(d) => Some(facts::chars(d)),
856 } }).collect();
857 let overflow = rt::text::string(self, &st.into, st.pointer.as_ref(), &sources, st.pos)?;
858 self.overflow_branch(overflow, &st.on_overflow, &st.not_on_overflow)
859 }
860
861 fn unstring(&mut self, u: &'p Unstring) -> R<Flow> {
862 let delimiters: Vec<_> = u.delimiters.iter().map(|(all, d)| (*all, facts::chars(d))).collect();
863 let into: Vec<_> = u.into.iter().map(|i| UnstringField { target: &i.target, delimiter: i.delimiter_in.as_ref(), count: i.count_in.as_ref() }).collect();
864 let overflow = rt::text::unstring(self, &u.source, u.pointer.as_ref(), &delimiters, &into, u.tallying.as_ref(), u.pos)?;
865 self.overflow_branch(overflow, &u.on_overflow, &u.not_on_overflow)
866 }
867
868 fn inspect(&mut self, i: &Inspect) -> R<()> {
869 let tallying: Vec<_> = i.tallying.iter().map(facts::inspect_phrase).collect();
870 let Operand::Ref(target) = &i.target else {
871 return rt::text::tally(self, &&i.target, &tallying, i.pos);
872 };
873 let replacing: Vec<_> = i.replacing.iter().map(facts::inspect_phrase).collect();
874 let converting = i.converting.as_ref().map(|(from, to, bounds)| Converting { table: ConvertTable::Operands { from: facts::chars(from), to: facts::chars(to) }, bounds: facts::bounds(bounds) });
875 rt::text::inspect(self, target, &tallying, &replacing, converting.as_ref(), i.pos)
876 }
877
878 fn search(&mut self, se: &'p Search) -> R<Flow> {
879 let pos = se.pos;
880 let Resolved::Item(t) = self.resolve(&se.table)? else {
881 return Err(Abend::ironwork(format!("SEARCH {}: not a table", se.table.name), pos));
882 };
883 let layout = self.layout;
884 let table = &layout.items[t];
885 let count = self.occurrences(t, pos)? as i64;
886 let Some(index) = table.search_index(se.varying.as_ref(), pos) else {
887 return Err(Abend::ironwork(format!("SEARCH {}: the table has no INDEXED BY", se.table.name), pos));
888 };
889 let index_expr = Expr::Operand(Operand::Ref(index.clone()));
890 if !se.all {
891 loop {
892 let i = self.integer(&index_expr, pos)?;
893 if i < 1 || i > count {
894 return match &se.at_end {
895 Some(body) => self.run_block(body),
896 None => Ok(Flow::Next),
897 };
898 }
899 for (cond, body) in &se.whens {
900 if self.condition(cond, pos)? {
901 return self.run_block(body);
902 }
903 }
904 self.set_integer(&index, i + 1, pos)?;
905 if let Some(v) = &se.varying
906 && v.name != index.name
907 {
908 let current = self.integer(&Expr::Operand(Operand::Ref(v.clone())), pos)?;
909 self.set_integer(v, current + 1, pos)?;
910 }
911 }
912 }
913 let (cond, body) = &se.whens[0];
914 let mut terms = Vec::new();
915 flatten_and(cond, &mut terms);
916 let (mut low, mut high) = (1i64, count);
917 while low <= high {
918 let mid = (low + high) / 2;
919 self.set_integer(&index, mid, pos)?;
920 let mut order = Ordering::Equal;
921 for (ascending, key) in &table.keys {
922 let Some((subject, value)) = key_term(&terms, &key.name) else { continue };
923 let o = self.compare(subject, value, pos)?;
924 order = if *ascending { o } else { o.reverse() };
925 if order != Ordering::Equal {
926 break;
927 }
928 }
929 match order {
930 Ordering::Less => low = mid + 1,
931 Ordering::Greater => high = mid - 1,
932 Ordering::Equal => {
933 return if self.condition(cond, pos)? {
934 self.run_block(body)
935 } else {
936 match &se.at_end {
937 Some(b) => self.run_block(b),
938 None => Ok(Flow::Next),
939 }
940 };
941 }
942 }
943 }
944 match &se.at_end {
945 Some(b) => self.run_block(b),
946 None => Ok(Flow::Next),
947 }
948 }
949
950 fn address_of(&mut self, r: &Ref) -> R<u32> {
952 if let Ok(Resolved::Item(i)) = self.resolve(r)
953 && let Some(l) = self.layout.items[i].linkage
954 && self.linkage[l as usize].is_none()
955 {
956 return Ok(0);
957 }
958 let loc = self.locate(r)?;
959 Ok(ADDRESS_BASE + loc.offset as u32)
960 }
961
962 fn program_name(&mut self, op: &Operand, pos: Pos) -> R<String> {
963 Ok(match self.operand(op, pos)? {
964 Val::Bytes(b) => self.page.decode(&b).trim().to_ascii_uppercase(),
965 _ => return Err(Abend::ironwork("a program name must be alphanumeric", pos)),
966 })
967 }
968
969 pub(crate) fn sink(&mut self, kind: &'static str, pos: Pos, operand: &str) {
971 let file = self.event_file(pos);
972 let input = self.unit.input_at_sink();
973 self.unit.notify(Event::Sink { kind, file: &file, line: pos.line, operand, input });
974 }
975
976 fn event_file(&self, pos: Pos) -> String {
979 match (pos.file, &self.unit.programs[self.me].source) {
980 (0, Some(path)) => path.to_str().unwrap_or_default().to_owned(),
981 (i, _) => self.program.sources.get(i as usize).cloned().unwrap_or_default(),
982 }
983 }
984
985 fn arguments_text(&mut self, c: &Call) -> R<String> {
986 callee::arguments_text(self, &call_args(&c.using), c.pos)
987 }
988
989 fn call(&mut self, c: &'p Call) -> R<Flow> {
990 let pos = c.pos;
991 let (name, dynamic) = match self.entry_pointer(&c.target)? {
992 Some(entry) => (entry.name, entry.dynamic),
993 None => {
994 if let Some(flow) = self.call_through_pointer(c)? {
995 return Ok(flow);
996 }
997 let name = self.program_name(&c.target, pos)?;
998 let variable = !matches!(c.target, Operand::Literal(_));
999 if variable && self.unit.observed() {
1000 self.sink("dynamic-program-load", pos, &name);
1001 }
1002 (name, self.options.dynam || variable)
1003 }
1004 };
1005 if self.unit.observed()
1007 && OS_COMMAND_ROUTINES.contains(&name.as_str())
1008 && let Ok(text) = self.arguments_text(c)
1009 {
1010 self.sink("os-command", pos, &text);
1011 }
1012 if self.options.le_services == LeServices::Bind && crate::le::provides(&name) {
1013 return self.le_call(c, &name);
1014 }
1015 let strict = self.options.program_scope == ProgramScope::Strict;
1016 let found = if strict && callee::names(self.program.hidden.iter().map(String::as_str), &name) {
1017 Err(LoadError::NotFound)
1018 } else {
1019 self.unit.load_entry(&name, rt::callee::entry_copy(dynamic, self.options.dialect_of(Switched::EntryCalls)))
1020 };
1021 let found = found.and_then(|(index, entry)| {
1022 let contained = self.unit.programs[index].compiled.as_deref().is_some_and(|t| !t.program.containers.is_empty());
1023 match strict && contained && !callee::names(self.program.callable.iter().map(String::as_str), &name) {
1024 true => Err(LoadError::NotFound),
1025 false => Ok((index, entry)),
1026 }
1027 });
1028 let (index, entry) = match found {
1029 Ok(i) => i,
1030 Err(LoadError::NotFound) if crate::le::provides(&name) => return self.le_call(c, &name),
1031 Err(LoadError::NotFound) => {
1032 if let Some(flow) = self.virtual_print(c, &name)? {
1033 return Ok(flow);
1034 }
1035 return match &c.on_exception {
1036 Some(body) => self.run_block(body),
1037 None => Err(crate::le::not_found(&format!("CALL {name}"), &name, dynamic, pos)),
1038 };
1039 }
1040 Err(LoadError::Compile(message)) => return Err(Abend::ironwork(format!("CALL {name}: {message}"), pos)),
1041 };
1042 let held = self.unit.programs[index].compiled.clone();
1043 let Some(compiled) = held.as_deref().or(self.first) else {
1044 return Err(Abend::ironwork(format!("CALL {name}: the run unit's first program cannot be CALLed from a function or a method"), pos));
1045 };
1046 self.unit.programs[index].dynamic |= dynamic;
1047 let program = &compiled.program;
1048 if self.unit.programs[index].active && !program.recursive {
1049 let unit = program.containers.last().map_or(&program.id, |c| &c.id);
1050 return Err(callee::recursive_call(&program.id, unit, pos));
1051 }
1052 self.unit.enter(pos)?;
1053 let result = self.call_nested(c, index, entry, compiled, dynamic);
1054 self.unit.depth -= 1;
1055 if let Some(flow) = result? {
1056 return Ok(flow);
1057 }
1058 match &c.not_on_exception {
1059 Some(body) => self.run_block(body),
1060 None => Ok(Flow::Next),
1061 }
1062 }
1063
1064 fn virtual_print(&mut self, c: &'p Call, name: &str) -> R<Option<Flow>> {
1068 use rt::virtual_printer::{self, Job};
1069 if !virtual_printer::ROUTINES.contains(&name) {
1070 return Ok(None);
1071 }
1072 let Some(printer) = self.unit.dds.get(virtual_printer::DD) else { return Ok(None) };
1073 let Some(job) = self.arguments_text(c).ok().as_deref().and_then(Job::parse) else { return Ok(None) };
1074 let dds = self.unit.dds.clone();
1075 let status: i16 = match virtual_printer::print(&dds, &printer, &job, &mut |event| self.unit.notify(event)) {
1076 Ok(()) => 0,
1077 Err(why) => {
1078 let _ = writeln!(self.unit.err, "ironwork: {}: CALL {name}: the virtual printer printed nothing: {why}", c.pos);
1079 1
1080 }
1081 };
1082 match &c.returning {
1083 Some(target) => {
1084 let dest = self.locate(target)?;
1085 self.assign(dest, Val::Num(Fixed::new(i128::from(status), Places::new(9, 0))), None, c.pos)?;
1086 }
1087 None => self.unit.write(RETURN_CODE, &status.to_be_bytes()),
1088 }
1089 Ok(Some(match &c.not_on_exception {
1090 Some(body) => self.run_block(body)?,
1091 None => Flow::Next,
1092 }))
1093 }
1094
1095 fn entry_pointer(&mut self, target: &Operand) -> R<Option<rt::set::Entry>> {
1097 let Operand::Ref(r) = target else { return Ok(None) };
1098 let Ok(Resolved::Item(item)) = self.resolve(r) else { return Ok(None) };
1099 if self.layout.items[item].kind != Kind::ProgramPointer {
1100 return Ok(None);
1101 }
1102 let loc = self.locate(r)?;
1103 let Ok(value) = <[u8; 4]>::try_from(store::bytes(&self.unit.mem, loc)).map(u32::from_be_bytes) else { return Ok(None) };
1104 Ok(rt::set::entry_of(&self.unit.entries, value).cloned())
1105 }
1106
1107 fn entry_named(&mut self, entry: &Operand, pos: Pos) -> R<(String, bool)> {
1110 let name = self.program_name(entry, pos)?;
1111 let variable = !matches!(entry, Operand::Literal(_));
1112 if variable && self.unit.observed() {
1113 self.sink("dynamic-program-load", pos, &name);
1114 }
1115 let dynamic = self.options.dynam || variable;
1116 match self.unit.load_entry(&name, rt::callee::entry_copy(dynamic, self.options.dialect_of(Switched::EntryCalls))) {
1117 Ok(_) => Ok((name, dynamic)),
1118 Err(LoadError::NotFound) if crate::le::provides(&name) => Ok((name, dynamic)),
1119 Err(LoadError::NotFound) => Err(crate::le::not_found(&format!("SET TO ENTRY {name}"), &name, dynamic, pos)),
1120 Err(LoadError::Compile(message)) => Err(Abend::ironwork(format!("SET TO ENTRY {name}: {message}"), pos)),
1121 }
1122 }
1123
1124 fn call_nested(&mut self, c: &'p Call, index: usize, entry: Option<usize>, compiled: &Compiled, dynamic: bool) -> R<Option<Flow>> {
1128 let pos = c.pos;
1129 let mark = self.unit.mem.len();
1130 let (addresses, lengths) = callee::arguments(self, &call_args(&c.using), pos)?;
1131 self.parmcheck_set();
1132 let suspends = dynamic && compiled.program.containers.is_empty();
1134 let containers = self.containers_of(&compiled.program);
1135 let by = By::Call { initial: compiled.program.initial };
1136 let (ending, returned) = callee::run(self, &Callee { index, by, mark: Some(mark), pos, lengths: &lengths }, |m| {
1137 let mut callee = Machine::activation_within(compiled, index, &mut *m.unit, false, containers)?;
1138 let entry = entry.and_then(|k| compiled.entries.get(k));
1139 callee.bind_linkage(&[], entry.map_or(&compiled.program.using, |e| &e.using), &addresses, true);
1140 (callee.cics_handlers, callee.first) = (m.cics_handlers.lend(suspends), m.first);
1141 let ending = callee.run_called(entry.map(|e| (e.paragraph, e.statement)));
1142 m.cics_handlers.take_back(&mut callee.cics_handlers, suspends && ending.is_ok());
1143 let returned = match (&compiled.program.returning, &ending) {
1144 (Some(item), Ok(_)) => Some(callee.returned(item, pos)?),
1145 _ => None,
1146 };
1147 Ok::<_, Abend>((ending, returned))
1148 })?;
1149 if ending? == Ending::StopRun {
1150 return Ok(Some(Flow::End(Ending::StopRun)));
1151 }
1152 if crate::cics::level_ended(self.unit) {
1153 return Ok(Some(Flow::End(Ending::Goback)));
1154 }
1155 self.parmcheck_test(c, &addresses, |unit| unit.programs[index].name.clone())?;
1156 if let (Some(target), Some(val)) = (&c.returning, returned) {
1157 let dest = self.locate_written(|m| m.locate(target))?;
1158 self.assign(dest, val, None, pos)?;
1159 }
1160 Ok(None)
1161 }
1162
1163 pub(crate) fn bind(&mut self, addresses: &[Option<usize>]) {
1165 let program = self.program;
1166 self.bind_linkage(&[], &program.using, addresses, false);
1167 }
1168
1169 fn bind_linkage(&mut self, records: &[(usize, usize)], using: &[Param], addresses: &[Option<usize>], returning: bool) {
1173 let layout = self.layout;
1174 let ordinal = |name: &str| layout.linkage_roots.iter().position(|&i| layout.items[i].name.as_deref() == Some(name));
1175 let using = using.iter().map(|param| ordinal(¶m.name)).collect();
1176 let returning = self.program.returning.as_deref().filter(|_| returning).and_then(ordinal).map(|o| (o, layout.items[layout.linkage_roots[o]].size as usize));
1177 Bindings { records, using, addresses, returning }.bind(self.unit, &mut self.linkage);
1178 }
1179
1180 fn returned(&mut self, name: &str, pos: Pos) -> R<Val> {
1181 let r = Ref { name: name.to_owned(), qualifiers: Vec::new(), subscripts: Vec::new(), refmod: None, pos };
1182 let loc = self.locate(&r)?;
1183 store::read(&self.facts(), &self.unit.mem, loc, pos)
1184 }
1185
1186 fn set(&mut self, set: &SetStmt, pos: Pos) -> R<()> {
1187 match set {
1188 SetStmt::ConditionTrue(targets) | SetStmt::ConditionFalse(targets) => {
1189 let truth = matches!(set, SetStmt::ConditionTrue(_));
1190 for r in targets {
1191 let Resolved::Condition(index) = self.resolve(r)? else {
1192 return Err(Abend::ironwork(format!("SET {} TO {}: not a condition-name", r.name, if truth { "TRUE" } else { "FALSE" }), pos));
1193 };
1194 let condition = &self.layout.conditions[index];
1195 let value = if truth { condition.values.first().map(|(v, _)| v) } else { condition.false_value.as_ref() };
1196 let Some(value) = value else { continue };
1197 let dest = self.locate_written(|m| m.locate_item(condition.item, r, false))?;
1198 let val = self.literal_value(value, pos)?;
1199 self.assign(dest, val, None, pos)?;
1200 }
1201 }
1202 SetStmt::To { targets, value } => {
1203 for r in targets {
1204 let dest = self.locate_written(|m| m.locate(r))?;
1205 let (val, src) = self.operand_with_loc(value, pos)?;
1206 let (val, src) = rt::set::to(dest, val, src, pos)?;
1207 self.assign(dest, val, src, pos)?;
1208 }
1209 }
1210 SetStmt::Entry { targets, entry } => {
1211 let (name, dynamic) = self.entry_named(entry, pos)?;
1212 let value = rt::set::entry(&mut self.unit.entries, &name, dynamic, pos)?;
1213 for r in targets {
1214 let dest = self.locate_written(|m| m.locate(r))?;
1215 self.assign(dest, Val::Address(value), None, pos)?;
1216 }
1217 }
1218 SetStmt::AddressOf { targets, value } => {
1219 let val = self.operand(value, pos)?;
1220 let offset = rt::set::address(val, self.unit.mem.len(), pos)?;
1221 for r in targets {
1222 let Resolved::Item(i) = self.resolve(r)? else {
1223 return Err(Abend::ironwork(format!("SET ADDRESS OF {}: not a data item", r.name), pos));
1224 };
1225 let Some(ordinal) = self.layout.items[i].linkage.filter(|_| self.layout.items[i].parent.is_none()) else {
1226 return Err(Abend::ironwork(format!("SET ADDRESS OF {}: only a LINKAGE record can be given an address", r.name), pos));
1227 };
1228 self.linkage[ordinal as usize] = offset;
1229 }
1230 }
1231 SetStmt::UpDown { targets, down, by } => {
1232 let by = self.integer(by, pos)?;
1233 let targets: Vec<&Ref> = targets.iter().collect();
1234 rt::set::up_down(self, by, *down, &targets, pos)?;
1235 }
1236 SetStmt::Switches(_) => return Err(Abend::ironwork("SET ... TO ON or OFF reached the interpreter, which runs it as the SET ... TO TRUE compile makes it", pos)),
1237 }
1238 Ok(())
1239 }
1240
1241 fn accept(&mut self, target: &Ref, from: AcceptFrom, pos: Pos) -> R<bool> {
1243 let dest = self.locate_written(|m| m.locate_receiving(target))?;
1244 rt::accept::accept(&self.facts(), self.unit, dest, from, &target.name, pos)
1245 }
1246
1247 fn function(&mut self, f: &FunctionCall, within: Within) -> R<Val> {
1248 if let Some(udf) = self.user_function(&f.name) {
1249 let value = self.invoke_function(udf, f)?;
1250 return self.function_refmod(f, value);
1251 }
1252 if let Some(value) = self.storage_function(f)? {
1253 return self.function_refmod(f, value);
1254 }
1255 let mut args = self.function_arguments(f, within)?;
1256 let side = match f.modifier.as_deref() {
1257 Some("LEADING") => Some(TrimSide::Leading),
1258 Some("TRAILING") => Some(TrimSide::Trailing),
1259 _ => None,
1260 };
1261 let value = rt::intrinsic::function::evaluate(&mut intrinsic::Call { machine: self, f }, &f.name, side, &mut args, f.pos)?;
1262 self.function_refmod(f, value)
1263 }
1264
1265 fn function_refmod(&mut self, f: &FunctionCall, value: Val) -> R<Val> {
1266 let pos = f.pos;
1267 let Some(rm) = &f.refmod else { return Ok(value) };
1268 rt::intrinsic::function::refmod(value, pos, || {
1269 let start = self.integer(&rm.start, pos)?;
1270 let length = match &rm.length {
1271 Some(l) => Some(self.integer(l, pos)?),
1272 None => None,
1273 };
1274 Ok((start, length))
1275 })
1276 }
1277
1278 fn expr_value(&mut self, e: &Expr, pos: Pos) -> R<Val> {
1279 match e {
1280 Expr::Operand(op) => self.operand(op, pos),
1281 _ if self.uses_float(e)? || (divided_exponent(e) && self.static_dmax(e) > 0) => Ok(Val::Float(self.eval_float(e, self.options.arith.float_intermediate(), pos)?)),
1282 _ => {
1283 let dmax = self.dmax(e)?;
1284 Ok(Val::Num(self.eval_fixed(e, dmax, pos)?))
1285 }
1286 }
1287 }
1288
1289 fn operand_kind(&mut self, op: &Operand) -> R<Option<Kind>> {
1290 Ok(match op {
1291 Operand::Ref(r) => Some(self.locate(r)?.kind),
1292 Operand::Function(f) => self.user_function(&f.name).map(|u| u.result.kind),
1293 _ => None,
1294 })
1295 }
1296
1297 fn uses_float(&mut self, e: &Expr) -> R<bool> {
1299 Ok(match e {
1300 Expr::Operand(Operand::Function(f)) => self.is_floating_point(f)?,
1301 Expr::Operand(op) => matches!(self.operand_kind(op)?, Some(Kind::Float(_))),
1302 Expr::Neg(inner) => self.uses_float(inner)?,
1303 Expr::Bin(a, BinOp::Pow, b) => self.uses_float(a)? || self.uses_float(b)? || decimal_exponent(b, &mut |op| Ok::<_, Abend>(self.static_scale(op)))?,
1304 Expr::Bin(a, _, b) => self.uses_float(a)? || self.uses_float(b)?,
1305 })
1306 }
1307
1308 fn static_scale(&mut self, op: &Operand) -> u32 {
1310 let kind = match op {
1311 Operand::Ref(r) if r.refmod.is_none() => match self.resolve(r) {
1312 Ok(Resolved::Item(i)) => Some(self.layout.items[i].kind),
1313 _ => None,
1314 },
1315 Operand::Function(f) => self.user_function(&f.name).map(|u| u.result.kind),
1316 _ => None,
1317 };
1318 kind.and_then(Kind::digits_scale).map_or(0, |(_, s)| s)
1319 }
1320
1321 fn static_dmax(&mut self, e: &Expr) -> u32 {
1323 match e {
1324 Expr::Operand(Operand::Literal(Literal::Number(t))) => literal_fixed(t).map_or(0, |f| f.places.dec),
1325 Expr::Operand(op) => self.static_scale(op),
1326 Expr::Neg(inner) => self.static_dmax(inner),
1327 Expr::Bin(a, BinOp::Div | BinOp::Pow, _) => self.static_dmax(a),
1328 Expr::Bin(a, _, b) => self.static_dmax(a).max(self.static_dmax(b)),
1329 }
1330 }
1331
1332 fn dmax(&mut self, e: &Expr) -> R<u32> {
1335 Ok(match e {
1336 Expr::Operand(Operand::Literal(Literal::Number(t))) => literal_fixed(t).map_or(0, |f| f.places.dec),
1337 Expr::Operand(Operand::Function(f)) => function_dmax(self.layout, self.functions, f),
1338 Expr::Operand(op) => self.operand_kind(op)?.and_then(Kind::digits_scale).map_or(0, |(_, s)| s),
1339 Expr::Neg(inner) => self.dmax(inner)?,
1340 Expr::Bin(a, BinOp::Div | BinOp::Pow, _) => self.dmax(a)?,
1341 Expr::Bin(a, _, b) => self.dmax(a)?.max(self.dmax(b)?),
1342 })
1343 }
1344
1345 fn eval_fixed(&mut self, e: &Expr, dmax: u32, pos: Pos) -> R<Fixed> {
1346 self.eval_fixed_at(e, dmax, dmax, dmax, pos)
1347 }
1348
1349 fn eval_fixed_at(&mut self, e: &Expr, last: u32, inner: u32, dmax: u32, pos: Pos) -> R<Fixed> {
1352 let arith = self.options.arith;
1353 match e {
1354 Expr::Operand(Operand::Function(f)) => {
1355 let val = self.function(f, Within::Fixed(dmax))?;
1356 arith::fixed_operand(val, last, pos)
1357 }
1358 Expr::Operand(op) => {
1359 let val = self.operand(op, pos)?;
1360 arith::fixed_operand(val, last, pos)
1361 }
1362 Expr::Neg(operand) => Ok(arith::fixed_neg(self.eval_fixed_at(operand, inner, inner, dmax, pos)?)),
1363 Expr::Bin(a, op, b) => {
1364 let x = self.eval_fixed_at(a, inner, inner, dmax, pos)?;
1365 if *op == BinOp::Pow {
1366 let n = self.integer(b, pos)?;
1367 return arith::pow(x, n, last, arith, pos);
1368 }
1369 let y = self.eval_fixed_at(b, inner, inner, dmax, pos)?;
1370 if arith::divides_by_zero(*op, &y) {
1371 let binary = self.binary_division(a, b)?;
1372 return Err(arith::zero_divide(binary, pos));
1373 }
1374 arith::fixed_binop(x, *op, y, last, arith, pos)
1375 }
1376 }
1377 }
1378
1379 fn binary_division(&mut self, a: &Expr, b: &Expr) -> R<bool> {
1383 let mut items = 0;
1384 Ok(self.binary_operands(a, &mut items)? && self.binary_operands(b, &mut items)? && items > 0)
1385 }
1386
1387 fn binary_operands(&mut self, e: &Expr, items: &mut usize) -> R<bool> {
1388 Ok(match e {
1389 Expr::Operand(Operand::Literal(Literal::Number(t))) => !t.contains('.'),
1390 Expr::Operand(Operand::Literal(Literal::Figurative(Figurative::Zero))) => true,
1391 Expr::Operand(Operand::LengthOf(_)) => {
1392 *items += 1;
1393 true
1394 }
1395 Expr::Operand(op @ Operand::Ref(_)) => {
1396 let binary = matches!(self.operand_kind(op)?, Some(Kind::Binary { scale: 0, .. } | Kind::Index));
1397 *items += usize::from(binary);
1398 binary
1399 }
1400 Expr::Operand(_) => false,
1401 Expr::Neg(inner) => self.binary_operands(inner, items)?,
1402 Expr::Bin(x, _, y) => self.binary_operands(x, items)? && self.binary_operands(y, items)?,
1403 })
1404 }
1405
1406 fn eval_float(&mut self, e: &Expr, p: Precision, pos: Pos) -> R<Hfp> {
1407 match e {
1408 Expr::Operand(Operand::Function(f)) => {
1409 let val = self.function(f, Within::Float(p))?;
1410 arith::float_operand(val, p, pos)
1411 }
1412 Expr::Operand(op) => {
1413 let val = self.operand(op, pos)?;
1414 arith::float_operand(val, p, pos)
1415 }
1416 Expr::Neg(inner) => Ok(arith::float_neg(self.eval_float(inner, p, pos)?)),
1417 Expr::Bin(a, op, b) => {
1418 let (x, y) = (self.eval_float(a, p, pos)?, self.eval_float(b, p, pos)?);
1419 arith::float_binop(x, *op, y, p, pos)
1420 }
1421 }
1422 }
1423
1424 fn arithmetic(&mut self, computations: &[(Target, Expr)], remainder: Option<&(Target, Expr, Expr)>, handler: Option<&'p SizeError>, per_receiver: bool, pos: Pos) -> R<Flow> {
1429 let mut size_error = false;
1430 let mut places = Dmax::default();
1431 let mut float_receiver = false;
1434 for (t, e) in computations {
1435 let loc = self.locate(&t.r)?;
1436 float_receiver |= matches!(loc.kind, Kind::Float(_));
1437 places = places.max(Dmax::receiver(loc.kind.digits_scale().map_or(0, |(_, s)| s), t.rounded, self.options.extra_place())).with(self.dmax(e)?);
1438 }
1439 if let Some((t, dividend, _)) = remainder {
1440 let loc = self.locate(&t.r)?;
1441 places = places.with(loc.kind.digits_scale().map_or(0, |(_, s)| s)).with(self.dmax(dividend)?);
1442 }
1443 let dmax = places.last;
1444 let mut quotient_target: Option<Loc> = None;
1445 let mut results = Vec::with_capacity(computations.len());
1446 for (t, e) in computations {
1447 let own = |x: &Expr| per_receiver && matches!(x, Expr::Operand(Operand::Ref(r)) if *r == t.r);
1448 let float = float_receiver || self.uses_float(e)? || (divided_exponent(e) && dmax > 0);
1449 let (shared, with) = match e {
1450 Expr::Bin(a, op, b) if *op != BinOp::Pow && own(a) => (b.as_ref(), Some((*op, true))),
1451 Expr::Bin(a, op, b) if *op != BinOp::Pow && own(b) => (a.as_ref(), Some((*op, false))),
1452 _ => (e, None),
1453 };
1454 let outcome = if float {
1455 self.eval_float(shared, self.options.arith.float_intermediate(), pos).map(Val::Float)
1456 } else {
1457 let last = if with.is_some() { places.inner } else { dmax };
1458 self.eval_fixed_at(shared, last, places.inner, dmax, pos).map(Val::Num)
1459 };
1460 results.push((t, shared, with, outcome));
1461 }
1462 let operands = match remainder {
1463 Some((_, dividend, divisor)) => Some((self.eval_fixed(dividend, dmax, pos)?, self.eval_fixed(divisor, dmax, pos)?)),
1464 None => None,
1465 };
1466 for (t, shared, with, outcome) in results {
1467 let loc = self.locate(&t.r)?;
1468 quotient_target.get_or_insert(loc);
1469 let outcome = match (with, outcome) {
1470 (Some((op, receiver_first)), Ok(Val::Num(value))) => {
1471 let current = self.eval_fixed(&Expr::Operand(Operand::Ref(t.r.clone())), dmax, pos)?;
1472 let (x, y) = if receiver_first { (current, value) } else { (value, current) };
1473 if arith::divides_by_zero(op, &y) {
1474 let receiver = Expr::Operand(Operand::Ref(t.r.clone()));
1475 let binary = self.binary_division(&receiver, shared)?;
1476 Err(arith::zero_divide(binary, pos))
1477 } else {
1478 arith::fixed_binop(x, op, y, dmax, self.options.arith, pos).map(Val::Num)
1479 }
1480 }
1481 (Some((op, receiver_first)), Ok(Val::Float(value))) => {
1482 let p = self.options.arith.float_intermediate();
1483 let current = self.eval_float(&Expr::Operand(Operand::Ref(t.r.clone())), p, pos)?;
1484 let (x, y) = if receiver_first { (current, value) } else { (value, current) };
1485 arith::float_binop(x, op, y, p, pos).map(Val::Float)
1486 }
1487 (_, outcome) => outcome,
1488 };
1489 let Some(value) = arith::size_error(outcome, handler.is_some())? else {
1490 size_error = true;
1491 continue;
1492 };
1493 size_error |= store::store_value(&self.facts(), self.unit, loc, value, t.rounded, handler.is_some(), pos)?;
1494 }
1495 if let (Some((t, _, _)), Some((x, y)), Some(q_loc)) = (remainder, operands, quotient_target)
1496 && let Some(r) = arith::remainder(x, y, places_of(q_loc.kind).dec, dmax, self.options.arith, pos)?
1497 {
1498 let r_loc = self.locate(&t.r)?;
1499 size_error |= store::store_value(&self.facts(), self.unit, r_loc, Val::Num(r), false, handler.is_some(), pos)?;
1500 }
1501 if let Some(h) = handler {
1502 return self.run_block(if size_error { &h.on } else { &h.not_on });
1503 }
1504 Ok(Flow::Next)
1505 }
1506
1507 fn condition(&mut self, c: &Cond, pos: Pos) -> R<bool> {
1508 Ok(match c {
1509 Cond::Rel(a, op, b) => {
1510 let o = self.compare(a, b, pos)?;
1511 match op {
1512 RelOp::Eq => o == Ordering::Equal,
1513 RelOp::Ne => o != Ordering::Equal,
1514 RelOp::Lt => o == Ordering::Less,
1515 RelOp::Le => o != Ordering::Greater,
1516 RelOp::Gt => o == Ordering::Greater,
1517 RelOp::Ge => o != Ordering::Less,
1518 }
1519 }
1520 Cond::Not(inner) => !self.condition(inner, pos)?,
1521 Cond::And(a, b) => self.condition(a, pos)? && self.condition(b, pos)?,
1522 Cond::Or(a, b) => self.condition(a, pos)? || self.condition(b, pos)?,
1523 Cond::Class(e, class) => self.class(e, class, pos)?,
1524 Cond::NameOrRel { subject, op, negated, name } => match self.resolve(name)? {
1525 Resolved::Condition(_) => self.condition(&Cond::Name(name.clone()), pos)?,
1526 Resolved::Item(_) => self.condition(&Cond::Rel(subject.clone(), *op, Expr::Operand(Operand::Ref(name.clone()))), pos)? != *negated,
1527 },
1528 Cond::Name(r) => {
1529 let Resolved::Condition(index) = self.resolve(r)? else {
1530 return Err(Abend::ironwork(format!("{} is a data item, not a condition", r.name), r.pos));
1531 };
1532 let condition = &self.layout.conditions[index];
1533 let loc = self.locate_item(condition.item, r, false)?;
1534 self.numcheck(loc, false, r.pos)?;
1535 let mut subject = None;
1536 for (low, high) in &condition.values {
1537 let hit = match high {
1538 None => self.compare_value(loc, &mut subject, low, r.pos, pos)? == Ordering::Equal,
1539 Some(high) => self.compare_value(loc, &mut subject, low, r.pos, pos)? != Ordering::Less && self.compare_value(loc, &mut subject, high, r.pos, pos)? != Ordering::Greater,
1540 };
1541 if hit {
1542 return Ok(true);
1543 }
1544 }
1545 false
1546 }
1547 })
1548 }
1549
1550 fn class(&mut self, e: &Expr, class: &Class, pos: Pos) -> R<bool> {
1551 if let Class::Named(name) = class {
1552 let (Expr::Operand(Operand::Ref(r)), Some(bits)) = (e, self.layout.class(name)) else {
1553 return Err(Abend::ironwork(format!("class-name {name} tests a data item the program defines a CLASS for"), pos));
1554 };
1555 let loc = self.locate(r)?;
1556 return Ok(store::byte_class(&self.facts(), &self.unit.mem, loc, ByteClass::Set { bits }));
1557 }
1558 if let (Class::Numeric | Class::Alphabetic | Class::AlphabeticLower | Class::AlphabeticUpper | Class::Dbcs | Class::Kanji, Expr::Operand(Operand::Ref(r))) = (class, e) {
1559 let loc = self.locate(r)?;
1560 let test = match (class, loc.kind) {
1561 (Class::Numeric, Kind::Packed { signed, .. }) => ByteClass::Packed { signed },
1562 (Class::Numeric, Kind::Zoned { signed, sign: None, .. }) => ByteClass::Zoned { signed },
1563 (Class::Numeric, _) => ByteClass::Digits,
1564 (Class::AlphabeticLower, _) => ByteClass::AlphabeticLower,
1565 (Class::AlphabeticUpper, _) => ByteClass::AlphabeticUpper,
1566 (Class::Dbcs, _) => ByteClass::Dbcs,
1567 (Class::Kanji, _) => ByteClass::Kanji,
1568 (_, _) => ByteClass::Alphabetic,
1569 };
1570 return Ok(store::byte_class(&self.facts(), &self.unit.mem, loc, test));
1571 }
1572 let test = match class {
1573 Class::Positive => SignTest::Positive,
1574 Class::Negative => SignTest::Negative,
1575 _ => SignTest::Zero,
1576 };
1577 store::sign_test(self.expr_value(e, pos)?, test, pos)
1578 }
1579
1580 fn compare(&mut self, a: &Expr, b: &Expr, pos: Pos) -> R<Ordering> {
1582 for (zoned, other, zoned_first) in [(a, b, true), (b, a, false)] {
1583 if let Some(image) = self.zoned_bytes_against(zoned, other)? {
1584 if let Expr::Operand(Operand::Ref(r)) = zoned
1585 && self.checks_against(other)?
1586 {
1587 let loc = self.locate(r)?;
1588 self.numcheck(loc, false, r.pos)?;
1589 }
1590 let other = match other {
1591 Expr::Operand(Operand::Ref(r)) if self.zone_sensitive(other)? => {
1592 let loc = self.locate(r)?;
1593 self.numcheck(loc, false, r.pos)?;
1594 (Val::Bytes(Vec::new()), Some(loc))
1595 }
1596 _ => self.comparand_against(other, zoned, pos)?,
1597 };
1598 return store::compare_zoned_bytes(&self.facts(), &self.unit.mem, &image, other, zoned_first, pos);
1599 }
1600 }
1601 let (va, la) = self.comparand_against(a, b, pos)?;
1602 let (vb, lb) = self.comparand_against(b, a, pos)?;
1603 if let Some(o) = self.compare_references(a, b, (&va, la), (&vb, lb), pos)? {
1604 return Ok(o);
1605 }
1606 store::compare(&self.facts(), &self.unit.mem, (va, la), (vb, lb), pos)
1607 }
1608
1609 fn nonnumeric(&mut self, e: &Expr) -> R<bool> {
1611 Ok(match e {
1612 Expr::Operand(Operand::Literal(l)) => {
1613 matches!(l, Literal::Alnum(_) | Literal::Hex(_) | Literal::All(_)) || matches!(l, Literal::Figurative(f) if !matches!(f, Figurative::Zero | Figurative::Null))
1614 }
1615 Expr::Operand(Operand::Ref(o)) => store::nonnumeric(self.locate(o)?.kind),
1616 _ => false,
1617 })
1618 }
1619
1620 fn checks_against(&mut self, other: &Expr) -> R<bool> {
1623 Ok(!store::noalphnum(&self.options) || !self.nonnumeric(other)?)
1624 }
1625
1626 fn comparand_against(&mut self, e: &Expr, other: &Expr, pos: Pos) -> R<(Val, Option<Loc>)> {
1628 match e {
1629 Expr::Operand(Operand::Ref(r)) if !self.checks_against(other)? => {
1630 let loc = self.locate(r)?;
1631 Ok((store::read(&self.facts(), &self.unit.mem, loc, r.pos)?, Some(loc)))
1632 }
1633 _ => self.comparand(e, pos),
1634 }
1635 }
1636
1637 fn zoned_bytes_against(&mut self, e: &Expr, other: &Expr) -> R<Option<Vec<u8>>> {
1640 let Expr::Operand(Operand::Ref(r)) = e else { return Ok(None) };
1641 let nonnumeric = self.nonnumeric(other)?;
1642 let zones_count = (self.options.zones_compared_with_zero() || self.options.zones_compared_between_items())
1645 && self.zone_sensitive(e)?
1646 && match other {
1647 Expr::Operand(Operand::Literal(l)) => self.options.zones_compared_with_zero() && self.zero_literal(l),
1648 Expr::Operand(Operand::Ref(o)) => self.options.zones_compared_between_items() && self.zone_sensitive(other)? && self.locate(o)?.len == self.locate(r)?.len,
1649 _ => false,
1650 };
1651 if !nonnumeric && !zones_count {
1652 return Ok(None);
1653 }
1654 let loc = self.locate(r)?;
1655 Ok(store::compared_zoned_bytes(&self.facts(), &self.unit.mem, loc))
1656 }
1657
1658 fn zone_sensitive(&mut self, e: &Expr) -> R<bool> {
1660 let Expr::Operand(Operand::Ref(r)) = e else { return Ok(false) };
1661 let loc = self.locate(r)?;
1662 Ok(self.zone_sensitive_at(loc))
1663 }
1664
1665 fn zone_sensitive_at(&self, loc: Loc) -> bool {
1666 matches!(loc.kind, Kind::Zoned { scale: 0, signed: false, .. }) && self.layout.items.get(loc.item).is_none_or(|i| i.scaling == 0)
1667 }
1668
1669 fn zero_literal(&self, l: &Literal) -> bool {
1670 match l {
1671 Literal::Figurative(Figurative::Zero) => true,
1672 Literal::Number(t) => literal_fixed(t).is_some_and(|f| store::zero(&Val::Num(f))),
1673 _ => false,
1674 }
1675 }
1676
1677 fn compare_value(&mut self, loc: Loc, subject: &mut Option<(Val, Option<Loc>)>, value: &Literal, at: Pos, pos: Pos) -> R<Ordering> {
1681 let nonnumeric = matches!(value, Literal::Alnum(_) | Literal::Hex(_) | Literal::All(_)) || matches!(value, Literal::Figurative(f) if !matches!(f, Figurative::Zero | Figurative::Null));
1682 let zones_count = self.options.zones_compared_with_zero() && self.zone_sensitive_at(loc) && self.zero_literal(value);
1683 if (nonnumeric || zones_count)
1684 && let Some(image) = store::compared_zoned_bytes(&self.facts(), &self.unit.mem, loc)
1685 {
1686 let value = self.literal_value(value, pos)?;
1687 return store::compare_zoned_bytes(&self.facts(), &self.unit.mem, &image, (value, None), true, pos);
1688 }
1689 let subject = match subject {
1690 Some(s) => s.clone(),
1691 None => subject.insert((store::read(&self.facts(), &self.unit.mem, loc, at)?, Some(loc))).clone(),
1692 };
1693 self.compare_literal(&subject, value, pos)
1694 }
1695
1696 fn compare_literal(&mut self, subject: &(Val, Option<Loc>), literal: &Literal, pos: Pos) -> R<Ordering> {
1697 let value = self.literal_value(literal, pos)?;
1698 store::compare(&self.facts(), &self.unit.mem, subject.clone(), (value, None), pos)
1699 }
1700
1701 fn comparand(&mut self, e: &Expr, pos: Pos) -> R<(Val, Option<Loc>)> {
1702 match e {
1703 Expr::Operand(op) => self.operand_with_loc(op, pos),
1704 _ => Ok((self.expr_value(e, pos)?, None)),
1705 }
1706 }
1707
1708 fn display(&mut self, items: &[Operand], upon_console: bool, no_advancing: bool, pos: Pos) -> R<()> {
1709 let text = self.display_text(items, upon_console, pos)?;
1710 if self.unit.observed() {
1711 self.sink("log", pos, &text);
1712 }
1713 rt::display::write(&mut *self.unit.out, &text, no_advancing, pos)
1714 }
1715
1716 fn screen_at(&mut self, screen: &ScreenPhrases, pos: Pos) -> R<Option<(usize, usize)>> {
1720 let number = |m: &mut Self, o: &Operand| -> R<usize> { Ok(m.integer(&Expr::Operand(o.clone()), pos)?.max(0) as usize) };
1721 Ok(match &screen.at {
1722 None => None,
1723 Some(ScreenAt::Combined(o)) => Some(rt::crt::line_column(number(self, o)? as u64)),
1724 Some(ScreenAt::LineColumn { line, column }) => {
1725 let row = match line {
1726 Some(l) => number(self, l)?,
1727 None => self.unit.crt.as_ref().map_or(1, |c| c.borrow().cursor_position().0),
1728 };
1729 let column = match column {
1730 Some(c) => number(self, c)?,
1731 None => 1,
1732 };
1733 Some((row, column))
1734 }
1735 })
1736 }
1737
1738 fn display_text(&mut self, items: &[Operand], upon_console: bool, pos: Pos) -> R<String> {
1740 let mut text = String::new();
1741 for op in items {
1742 let shown = match op {
1743 Operand::Ref(r) => {
1744 let loc = self.locate(r)?;
1745 rt::display::place(&self.facts(), &self.unit.mem, loc, r.pos, upon_console)?
1746 }
1747 Operand::Literal(Literal::Number(t)) => rt::display::number(t, &self.facts()),
1748 other => {
1749 let val = self.operand(other, pos)?;
1750 rt::display::value(&self.facts(), val, pos, upon_console)?
1751 }
1752 };
1753 text.push_str(&shown);
1754 }
1755 Ok(text)
1756 }
1757
1758 fn initialize(&mut self, index: usize, offset: usize, with: &InitializeWith, pos: Pos) -> R<()> {
1760 let layout = self.layout;
1761 for (i, at) in layout.initialize_receivers(index, with.filler) {
1762 let item = &layout.items[i];
1763 let loc = Loc { offset: offset + at as usize, len: item.size as usize, kind: item.kind, item: i };
1764 match (with.initial_value(layout.category(i), item.value.is_some()), &item.value) {
1765 (Some(InitialValue::Value), Some(value)) => {
1766 let val = self.literal_value(value, pos)?;
1767 self.assign(Loc { kind: value_kind(item.kind, value), ..loc }, val, None, pos)?;
1768 }
1769 (Some(InitialValue::Replacing(by)), _) => {
1770 let (val, src) = self.operand_with_loc(by, pos)?;
1771 self.assign(loc, val, src, pos)?;
1772 }
1773 (Some(_), _) => self.assign(loc, initial_default(item.kind), None, pos)?,
1774 (None, _) => {}
1775 }
1776 }
1777 Ok(())
1778 }
1779}
1780
1781static NO_PHRASES: InitializeWith = InitializeWith { filler: false, value: Vec::new(), replacing: Vec::new(), default: false };
1782
1783fn call_args(using: &[Arg]) -> Vec<CallArg<&Ref, &Operand>> {
1786 using
1787 .iter()
1788 .map(|arg| match (arg.mode, &arg.value) {
1789 (_, None) => CallArg::Omitted,
1790 (ArgMode::Reference, Some(Operand::Ref(r))) => CallArg::Reference(r),
1791 (ArgMode::Value, Some(op)) => CallArg::Value(op),
1792 (_, Some(op)) => CallArg::Content(facts::chars(op)),
1793 })
1794 .collect()
1795}
1796
1797fn initial_default(kind: Kind) -> Val {
1800 match kind {
1801 Kind::Pointer => Val::Address(0),
1802 Kind::Alnum { .. } | Kind::AlnumEdited { .. } | Kind::National | Kind::Dbcs { .. } => Val::Fig(Figurative::Space),
1803 _ => Val::Fig(Figurative::Zero),
1804 }
1805}
1806
1807pub(crate) fn clearing(screen: &ScreenPhrases) -> rt::crt::Clearing {
1809 rt::crt::Clearing { screen: screen.blank_screen, line: screen.blank_line, to_line_end: screen.erase_eol, to_screen_end: screen.erase_eos }
1810}