1pub use rt::abend::{Abend, Ending};
6use crate::abend::{AbendCode, Signal};
7use crate::layout::{Item, Kind, Layout, Resolved};
8use rt::storage::{Loc, Val};
9pub(crate) use rt::storage::literal_fixed;
10use crate::unit::{ADDRESS_BASE, Event, LoadError, RETURN_CODE, RunUnit};
11use crate::Compiled;
12use numeric::precision::{self, Fixed, Places};
13use numeric::{Options, Trunc};
14use rt::fixed::{align, places_of, zoned_digits};
15use rt::arith;
16use rt::display::utf16_text;
17use rt::lir::{ByteClass, ConvertTable, Converting, SignTest, StringSource, TrimSide};
18use rt::loc;
19use rt::store;
20use rt::text::UnstringField;
21use std::cmp::Ordering;
22use std::collections::HashMap;
23use syntax::Pos;
24use syntax::ast::*;
25use zarch::decimal;
26use zarch::ebcdic::{self, CodePage, Collation};
27use zarch::hfp::{Hfp, Precision};
28
29mod cics;
30pub(crate) mod cics_bind;
31mod declaratives;
32mod facts;
33mod file_io;
34mod intrinsic;
35mod json;
36mod le_services;
37mod oo;
38mod perform;
39mod report;
40mod sort;
41pub(crate) mod sql;
42mod xml;
43
44type R<T> = Result<T, Abend>;
45
46const OS_COMMAND_ROUTINES: &[&str] = &["SYSTEM", "C$SYSTEM", "CBL_EXEC_RUN_UNIT", "CBL_GC_HOSTED", "BXPSYSTM"];
49
50enum Flow {
51 Next,
52 End(Ending),
53 GoTo(usize),
54 ExitParagraph,
55 ExitSection,
56 ExitPerform,
57 ExitPerformCycle,
58 NextSentence,
59 Resume(usize, usize),
61 Return(u64),
63}
64
65pub struct Machine<'p, 'u, 'w> {
66 program: &'p Program,
67 layout: &'p Layout,
68 options: Options,
69 ssrange: bool,
70 page: &'static CodePage,
71 collating: &'p crate::collating::Sequence,
72 when_compiled: rt::lir::CompileTime,
73 resolved: HashMap<(String, Vec<String>), Resolved>,
74 me: usize,
76 base: usize,
77 linkage: Vec<Option<usize>>,
79 local_base: usize,
81 main: bool,
83 cics_handlers: cics::Handlers,
85 report_writer: &'p crate::report::Writer,
86 carriage: &'p [Option<crate::printer::Carriage>],
88 oo: oo::Frame,
90 sort: Option<sort::Active>,
92 segment: u8,
94 declaratives: &'p crate::declaratives::Table,
95 uses: declaratives::State,
96 returns: perform::Returns,
97 xml: xml::Registers,
99 unit: &'u mut RunUnit<'w>,
100}
101
102enum Step {
103 Again,
104 Leave,
105 Out(Flow),
106}
107
108impl<'p, 'u, 'w> Machine<'p, 'u, 'w> {
109 pub fn activation(compiled: &'p Compiled, me: usize, unit: &'u mut RunUnit<'w>, main: bool) -> R<Self> {
112 let (base, fresh) = unit.activate(me, compiled.program.initial);
113 let mut m = Self {
114 program: &compiled.program,
115 layout: &compiled.layout,
116 options: compiled.options,
117 ssrange: compiled.ssrange,
118 page: compiled.options.code_page(),
119 collating: &compiled.collating,
120 when_compiled: compiled.when_compiled,
121 resolved: HashMap::new(),
122 me,
123 base,
124 linkage: vec![None; compiled.layout.linkage_roots.len()],
125 local_base: 0,
126 main,
127 cics_handlers: cics::Handlers::default(),
128 report_writer: &compiled.report_writer,
129 carriage: &compiled.carriage,
130 oo: oo::Frame::default(),
131 sort: None,
132 segment: 0,
133 declaratives: &compiled.declaratives,
134 uses: declaratives::State::default(),
135 returns: perform::Returns::new(compiled.program.paragraphs.len()),
136 xml: xml::Registers::default(),
137 unit,
138 };
139 if compiled.layout.local_size > 0 {
140 m.local_base = m.unit.push_temporary(&vec![0; compiled.layout.local_size as usize]);
141 m.initialize_values(true)?;
142 }
143 if fresh {
144 m.unit.mem[base..base + compiled.layout.size as usize].fill(0);
145 m.initialize_values(false)?;
146 m.unit.initialized(me);
147 }
148 Ok(m)
149 }
150
151 fn initialize_values(&mut self, local: bool) -> R<()> {
153 let base = if local { self.local_base } else { self.base };
154 for index in 0..self.layout.items.len() {
155 let item = &self.layout.items[index];
156 let Some(value) = item.value.clone().filter(|_| item.linkage.is_none() && item.local == local) else { continue };
157 let occurrences: u32 = item.dims.iter().map(|&(_, n)| n).product::<u32>().max(1);
158 let kind = match (item.kind, &value) {
160 (Kind::NumericEdited { .. }, Literal::Alnum(_) | Literal::Figurative(_) | Literal::All(_)) => Kind::Alnum { justified: false },
161 (kind, _) => kind,
162 };
163 for k in 0..occurrences {
164 let offset = base + item.offset as usize + self.occurrence_offset(item, k);
165 let loc = Loc { offset, len: item.size as usize, kind, item: index };
166 let val = self.literal_value(&value, item.pos)?;
167 self.assign(loc, val, None, item.pos)?;
168 }
169 }
170 Ok(())
171 }
172
173 fn occurrence_offset(&self, item: &Item, k: u32) -> usize {
174 loc::occurrence_offset(&item.dims, k)
175 }
176
177 pub fn run_procedure(&mut self) -> R<Ending> {
178 self.run_from(None)
179 }
180
181 fn enter_segment(&mut self, priority: u8) {
184 if priority == self.segment {
185 return;
186 }
187 self.segment = priority;
188 if priority >= 50 {
189 let program = self.program;
190 for (i, target) in self.unit.programs[self.me].altered.iter_mut().enumerate() {
191 if program.paragraphs[i].priority == priority {
192 *target = None;
193 }
194 }
195 }
196 }
197
198 fn run_sentences(&mut self, stmts: &'p [Stmt]) -> R<Flow> {
200 let mut i = 0;
201 while i < stmts.len() {
202 match self.exec(&stmts[i])? {
203 Flow::Next => i += 1,
204 Flow::NextSentence => i = stmts[i..].iter().position(|s| *s == Stmt::SentenceEnd).map_or(stmts.len(), |j| i + j + 1),
205 other => return Ok(other),
206 }
207 }
208 Ok(Flow::Next)
209 }
210
211 fn run_block(&mut self, stmts: &'p [Stmt]) -> R<Flow> {
212 for s in stmts {
213 match self.exec(s)? {
214 Flow::Next => {}
215 other => return Ok(other),
216 }
217 }
218 Ok(Flow::Next)
219 }
220
221 fn procedure(&self, p: &ProcName, pos: Pos) -> R<(usize, usize)> {
222 crate::procedure(self.program, p).map_err(|m| Abend::ironwork(m, pos))
223 }
224
225 fn exec(&mut self, s: &'p Stmt) -> R<Flow> {
227 if !self.declaratives.triggers.is_empty()
228 && let Some(pos) = declaratives::statement_pos(s)
229 {
230 self.uses.line = pos;
231 }
232 match self.statement(s) {
233 Err(Abend { code: AbendCode::Signal(Signal::DeclarativeExit), .. }) => Ok(self.declarative_exit()),
234 flow => flow,
235 }
236 }
237
238 fn statement(&mut self, s: &'p Stmt) -> R<Flow> {
239 match s {
240 Stmt::Move { from, to, pos } => {
241 for r in to {
242 let dest = self.locate_receiving(r)?;
243 let (val, src) = self.operand_with_loc(from, *pos)?;
244 self.assign(dest, val, src, *pos)?;
245 }
246 }
247 Stmt::Compute { targets, expr, size_error, pos } => {
248 let computations: Vec<(Target, Expr)> = targets.iter().map(|t| (t.clone(), expr.clone())).collect();
249 return self.arithmetic(&computations, None, size_error.as_ref(), false, *pos);
250 }
251 Stmt::Arith(a) => return self.arithmetic(&a.computations, a.remainder.as_ref(), a.size_error.as_ref(), true, a.pos),
252 Stmt::Corresponding(c) => return Err(Abend::ironwork("CORRESPONDING reached the interpreter unexpanded", c.pos)),
253 Stmt::If { cond, then, otherwise, pos } => {
254 let branch = if self.condition(cond, *pos)? { then } else { otherwise };
255 return self.run_block(branch);
256 }
257 Stmt::Evaluate { subjects, whens, other, pos } => {
258 for w in whens {
259 for alternative in &w.alternatives {
260 if self.alternative_matches(subjects, alternative, *pos)? {
261 return self.run_block(&w.body);
262 }
263 }
264 }
265 return self.run_block(other);
266 }
267 Stmt::PerformProc { from, thru, repeat, pos } => {
268 let (start, first_end) = self.procedure(from, *pos)?;
269 let end = match thru {
270 Some(t) => self.procedure(t, *pos)?.1,
271 None => first_end,
272 };
273 let statement = if matches!(repeat, Loop::Once) { self.after(s) } else { None };
274 return self.repeat(repeat, *pos, &mut |m: &mut Self| {
275 m.uses.line = *pos;
276 m.perform_range(start, end, None, statement)
277 });
278 }
279 Stmt::PerformInline { body, repeat, pos } => return self.repeat(repeat, *pos, &mut |m: &mut Self| m.run_block(body)),
280 Stmt::Display { items, no_advancing, pos } => self.display(items, *no_advancing, *pos)?,
281 Stmt::Open { files, pos } => {
282 for (mode, name) in files {
283 self.open_file(*mode, name, *pos)?;
284 }
285 }
286 Stmt::Close { files, pos } => {
287 for (name, closing) in files {
288 self.close_file_with(name, *closing, *pos)?;
289 }
290 }
291 Stmt::Read(r) => return self.read_stmt(r),
292 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),
293 Stmt::Rewrite { record, from, invalid, pos } => return self.rewrite_stmt(record, from.as_ref(), invalid, *pos),
294 Stmt::Delete { file, invalid, pos } => return self.delete_stmt(file, invalid, *pos),
295 Stmt::Start { file, key, invalid, pos } => return self.start_stmt(file, key.as_ref(), invalid, *pos),
296 Stmt::Initialize { targets, pos } => {
297 for r in targets {
298 let loc = self.locate(r)?;
299 if loc.item == usize::MAX {
300 self.write(loc, &vec![0; loc.len]);
301 } else {
302 self.initialize(loc.item, loc.offset, *pos)?;
303 }
304 }
305 }
306 Stmt::GoTo { target: Some(target), pos } => return Ok(Flow::GoTo(self.procedure(target, *pos)?.0)),
307 Stmt::GoTo { target: None, .. } | Stmt::Entry { .. } => {}
308 Stmt::GoToDepending { targets, on, pos } => {
309 let n = self.integer(&Expr::Operand(Operand::Ref(on.clone())), *pos)?;
310 if let Some(target) = usize::try_from(n).ok().and_then(|n| targets.get(n.wrapping_sub(1))) {
311 return Ok(Flow::GoTo(self.procedure(target, *pos)?.0));
312 }
313 }
314 Stmt::Alter { pairs, pos } => {
315 for (paragraph, target) in pairs {
316 let (at, to) = (self.procedure(paragraph, *pos)?.0, self.procedure(target, *pos)?.0);
317 let paragraphs = self.program.paragraphs.len();
318 let altered = &mut self.unit.programs[self.me].altered;
319 altered.resize(paragraphs, None);
320 altered[at] = Some(to);
321 }
322 if let Some(flow) = self.debug_alter(pairs, *pos)? {
323 return Ok(flow);
324 }
325 }
326 Stmt::Goback { .. } => return Ok(Flow::End(Ending::Goback)),
327 Stmt::ExitProgram { .. } if self.main => {}
328 Stmt::ExitProgram { .. } => return Ok(Flow::End(Ending::Goback)),
329 Stmt::Call(c) => return self.call(c),
330 Stmt::Cancel { targets, pos } => {
331 for t in targets {
332 let name = self.program_name(t, *pos)?;
333 self.cancel(&name, *pos)?;
334 }
335 }
336 Stmt::Set { set, pos } => self.set(set, *pos)?,
337 Stmt::Accept { target, from, pos } => self.accept(target, *from, *pos)?,
338 Stmt::String(st) => return self.string_stmt(st),
339 Stmt::Unstring(u) => return self.unstring(u),
340 Stmt::Inspect(i) => self.inspect(i)?,
341 Stmt::Search(se) => return self.search(se),
342 Stmt::Sorting(s) => return self.sorting(s),
343 Stmt::NextSentence => return Ok(Flow::NextSentence),
344 Stmt::Exec(block) if block.declarative() => {}
345 Stmt::Exec(block) if block.kind == ExecKind::Cics => return self.cics(block),
346 Stmt::Exec(block) if block.kind == ExecKind::Sql => return self.sql(block),
347 Stmt::Report(r) => return self.report_statement(r),
348 Stmt::Exec(block) => {
349 let kind = match block.kind {
350 ExecKind::Sql => "SQL",
351 ExecKind::Cics => "CICS",
352 ExecKind::Dli => "DLI",
353 ExecKind::Other => "",
354 };
355 return Err(Abend {
356 code: AbendCode::Exec,
357 message: format!("EXEC {kind} {} was reached: ironwork for COBOL checks EXEC statements but does not run them yet", block.command),
358 pos: block.pos,
359 file: None,
360 });
361 }
362 Stmt::Invoke(i) => return self.invoke(i),
363 Stmt::JsonGenerate(g) => return self.json_generate(g),
364 Stmt::XmlParse(x) => return self.xml_parse(x),
365 Stmt::XmlGenerate(x) => return self.xml_generate(x),
366 Stmt::JsonParse(j) => return self.json_parse(j),
367 Stmt::ExitMethod { .. } => return Ok(Flow::End(Ending::Goback)),
368 Stmt::SentenceEnd => {}
369 Stmt::StopRun { .. } => return Ok(Flow::End(Ending::StopRun)),
370 Stmt::Exit { kind: ExitKind::Paragraph, .. } => return Ok(Flow::ExitParagraph),
371 Stmt::Exit { kind: ExitKind::Section, .. } => return Ok(Flow::ExitSection),
372 Stmt::Exit { kind: ExitKind::Perform, .. } => return Ok(Flow::ExitPerform),
373 Stmt::Exit { kind: ExitKind::PerformCycle, .. } => return Ok(Flow::ExitPerformCycle),
374 Stmt::Continue | Stmt::Exit { kind: ExitKind::Plain, .. } => {}
375 }
376 Ok(Flow::Next)
377 }
378
379 fn alternative_matches(&mut self, subjects: &[Subject], objects: &[Object], pos: Pos) -> R<bool> {
380 for (subject, object) in subjects.iter().zip(objects) {
381 let hit = match (subject, object) {
382 (_, Object::Any) => true,
383 (Subject::Bool(b), Object::Bool(o)) => b == o,
384 (Subject::Bool(b), Object::Cond(c)) => self.condition(c, pos)? == *b,
385 (Subject::Cond(c), Object::Bool(o)) => self.condition(c, pos)? == *o,
386 (Subject::Cond(c), Object::Cond(d)) => self.condition(c, pos)? == self.condition(d, pos)?,
387 (Subject::Expr(e), Object::Value { not, from, thru }) => {
388 let inside = match thru {
389 None => self.compare(e, from, pos)? == Ordering::Equal,
390 Some(t) => self.compare(e, from, pos)? != Ordering::Less && self.compare(e, t, pos)? != Ordering::Greater,
391 };
392 inside != *not
393 }
394 _ => return Err(Abend::ironwork("a WHEN object of a different kind from its subject", pos)),
395 };
396 if !hit {
397 return Ok(false);
398 }
399 }
400 Ok(true)
401 }
402
403 fn repeat(&mut self, repeat: &'p Loop, pos: Pos, body: &mut dyn FnMut(&mut Self) -> R<Flow>) -> R<Flow> {
406 self.nest(pos)?;
407 let flow = self.repeat_nested(repeat, pos, body);
408 self.unit.depth -= 1;
409 flow
410 }
411
412 fn nest(&mut self, pos: Pos) -> R<()> {
413 self.unit.enter(pos)
414 }
415
416 fn repeat_nested(&mut self, repeat: &'p Loop, pos: Pos, body: &mut dyn FnMut(&mut Self) -> R<Flow>) -> R<Flow> {
417 let mut run = |m: &mut Self| -> R<Step> {
418 Ok(match body(m)? {
419 Flow::Next | Flow::ExitPerformCycle => Step::Again,
420 Flow::ExitPerform => Step::Leave,
421 other => Step::Out(other),
422 })
423 };
424 match repeat {
425 Loop::Once => match run(self)? {
426 Step::Out(f) => return Ok(f),
427 Step::Again | Step::Leave => {}
428 },
429 Loop::Times(count) => {
430 for _ in 0..self.integer(count, pos)?.max(0) {
431 match run(self)? {
432 Step::Again => {}
433 Step::Leave => break,
434 Step::Out(f) => return Ok(f),
435 }
436 }
437 }
438 Loop::Until { cond, test_after } => loop {
439 if !test_after && self.condition(cond, pos)? {
440 break;
441 }
442 match run(self)? {
443 Step::Again => {}
444 Step::Leave => break,
445 Step::Out(f) => return Ok(f),
446 }
447 if *test_after && self.condition(cond, pos)? {
448 break;
449 }
450 },
451 Loop::Varying { varying, after, test_after } => {
452 let levels: Vec<&'p Varying> = std::iter::once(&**varying).chain(after).collect();
453 let count = if *test_after { 1 } else { levels.len() };
454 for v in &levels[..count] {
455 self.vary_from(v, pos)?;
456 }
457 if let Step::Out(f) = self.vary(&levels, *test_after, pos, &mut run)? {
458 return Ok(f);
459 }
460 }
461 }
462 Ok(Flow::Next)
463 }
464
465 fn vary(&mut self, levels: &[&'p Varying], test_after: bool, pos: Pos, run: &mut dyn FnMut(&mut Self) -> R<Step>) -> R<Step> {
469 let Some((level, inner)) = levels.split_first() else { return run(self) };
470 loop {
471 if !test_after && self.condition(&level.until, pos)? {
472 return Ok(Step::Again);
473 }
474 if test_after && let Some(next) = inner.first() {
475 self.vary_from(next, pos)?;
476 }
477 match self.vary(inner, test_after, pos, run)? {
478 Step::Again => {}
479 other => return Ok(other),
480 }
481 if test_after && self.condition(&level.until, pos)? {
482 return Ok(Step::Again);
483 }
484 self.vary_by(level, pos)?;
485 if !test_after && let Some(next) = inner.first() {
486 self.vary_from(next, pos)?;
487 }
488 }
489 }
490
491 fn vary_from(&mut self, v: &Varying, pos: Pos) -> R<()> {
492 let var = self.locate(&v.var)?;
493 let start = self.expr_value(&v.from, pos)?;
494 self.assign(var, start, None, pos)
495 }
496
497 fn vary_by(&mut self, v: &Varying, pos: Pos) -> R<()> {
498 let var = self.locate(&v.var)?;
499 let step = Expr::Bin(Box::new(Expr::Operand(Operand::Ref(v.var.clone()))), BinOp::Add, Box::new(v.by.clone()));
500 let dmax = var.kind.digits_scale().map_or(0, |(_, s)| s).max(self.dmax(&step)?);
501 let next = self.eval_fixed(&step, dmax, pos)?;
502 self.store_fixed(var, &next, false, pos)
503 }
504
505 fn resolve(&mut self, r: &Ref) -> R<Resolved> {
506 let key = (r.name.clone(), r.qualifiers.clone());
507 if let Some(&hit) = self.resolved.get(&key) {
508 return Ok(hit);
509 }
510 let found = self.layout.resolve(&r.name, &r.qualifiers, r.pos).map_err(|e| Abend::ironwork(e.message, r.pos))?;
511 self.resolved.insert(key, found);
512 Ok(found)
513 }
514
515 fn locate(&mut self, r: &Ref) -> R<Loc> {
516 self.locate_as(r, false)
517 }
518
519 fn locate_receiving(&mut self, r: &Ref) -> R<Loc> {
523 self.locate_as(r, true)
524 }
525
526 fn locate_as(&mut self, r: &Ref, receiving: bool) -> R<Loc> {
527 if let Some(loc) = self.oo_register(r)?.or(self.xml_register(r)?) {
528 return Ok(loc);
529 }
530 if r.name == "RETURN-CODE" && r.qualifiers.is_empty() && !self.layout.items.iter().any(|i| i.name.as_deref() == Some("RETURN-CODE")) {
531 return Ok(Loc { offset: RETURN_CODE, len: 2, kind: Kind::Binary { digits: 4, scale: 0, signed: true, native: false }, item: usize::MAX });
532 }
533 let Resolved::Item(index) = self.resolve(r)? else {
534 return Err(Abend::ironwork(format!("{} is a condition-name, not a data item", r.name), r.pos));
535 };
536 self.locate_item(index, r, receiving)
537 }
538
539 fn locate_item(&mut self, index: usize, r: &Ref, receiving: bool) -> R<Loc> {
543 let layout = self.layout;
544 let item = &layout.items[index];
545 if r.subscripts.len() != item.dims.len() {
546 return Err(Abend::ironwork(format!("{} takes {} subscripts, not {}", r.name, item.dims.len(), r.subscripts.len()), r.pos));
547 }
548 let base = match item.linkage {
549 Some(l) => loc::linkage_base(self.linkage[l as usize], &r.name, r.pos)?,
550 None if item.local => self.local_base,
551 None => self.base,
552 };
553 let mut offset = (base + item.offset as usize) as i64;
554 for (&(stride, count), sub) in item.dims.iter().zip(&r.subscripts) {
555 let s = self.integer(sub, r.pos)?;
556 offset += loc::subscript(s, stride, self.ssrange.then_some(count), &r.name, r.pos)?;
557 }
558 let (mut len, mut kind) = (item.size as i64, item.kind);
559 if let Some(t) = item.odo
560 && !(receiving && r.refmod.is_none() && self.object_within(t, index)?)
561 {
562 let table = &layout.items[t];
563 let current = self.occurrences(t, r.pos)?;
564 len = loc::odo_len(len, table.occurs, current, table.size);
565 }
566 if let Some(rm) = &r.refmod {
567 let start = self.integer(&rm.start, r.pos)?;
568 let length = match &rm.length {
569 Some(l) => Some(self.integer(l, r.pos)?),
570 None => None,
571 };
572 let unit = if kind == Kind::National { 2 } else { 1 };
574 let (from, length) = loc::refmod(len / unit, start, length, self.ssrange, &r.name, r.pos)?;
575 offset += from * unit;
576 len = length * unit;
577 if kind != Kind::National {
578 kind = Kind::Alnum { justified: false };
579 }
580 }
581 let (offset, len) = loc::within(offset, len, self.unit.mem.len(), &r.name, r.pos)?;
582 Ok(Loc { offset, len, kind, item: index })
583 }
584
585 fn object_within(&mut self, t: usize, group: usize) -> R<bool> {
587 let layout = self.layout;
588 let Some(object) = &layout.items[t].depending_on else { return Ok(false) };
589 let Resolved::Item(mut at) = self.resolve(object)? else { return Ok(false) };
590 loop {
591 if at == group {
592 return Ok(true);
593 }
594 match layout.items[at].parent {
595 Some(p) => at = p,
596 None => return Ok(false),
597 }
598 }
599 }
600
601 fn occurrences(&mut self, table: usize, pos: Pos) -> R<u32> {
604 let layout = self.layout;
605 let item = &layout.items[table];
606 let Some(object) = &item.depending_on else { return Ok(item.occurs) };
607 let count = self.integer(&Expr::Operand(Operand::Ref(object.clone())), pos)?;
608 loc::occurrences(count, item.occurs, self.ssrange, &object.name, pos)
609 }
610
611 fn bytes(&self, loc: Loc) -> &[u8] {
612 store::bytes(&self.unit.mem, loc)
613 }
614
615 fn write(&mut self, loc: Loc, bytes: &[u8]) {
616 store::write(&mut self.unit.mem, loc, bytes);
617 }
618
619 fn integer(&mut self, e: &Expr, pos: Pos) -> R<i64> {
620 let dmax = self.dmax(e)?;
621 let v = self.eval_fixed(e, dmax, pos)?;
622 let whole = align(&v, 0, false).and_then(|m| m.to_u128()).and_then(|m| i64::try_from(m).ok());
623 let whole = whole.ok_or_else(|| Abend::ironwork("an integer operand beyond 64 bits", pos))?;
624 Ok(if v.negative { -whole } else { whole })
625 }
626
627 fn literal_value(&self, lit: &Literal, pos: Pos) -> R<Val> {
628 Ok(match lit {
629 Literal::Alnum(s) => Val::Bytes(self.page.encode(s).map_err(|e| Abend::ironwork(e.to_string(), pos))?),
630 Literal::Hex(b) => Val::Bytes(b.clone()),
631 Literal::National(s) => Val::National(s.encode_utf16().flat_map(u16::to_be_bytes).collect()),
632 Literal::Number(t) => Val::Num(literal_fixed(t).ok_or_else(|| Abend::ironwork(format!("the literal {t} has more than 31 digits"), pos))?),
633 Literal::Figurative(f) => Val::Fig(*f),
634 Literal::All(inner) => match self.literal_value(inner, pos)? {
635 Val::Bytes(b) => Val::All(b),
636 Val::Fig(f) => Val::Fig(f),
637 _ => return Err(Abend::ironwork("ALL takes an alphanumeric literal", pos)),
638 },
639 })
640 }
641
642 fn decimal_point(&self) -> char {
644 if self.program.environment.decimal_point_comma { ',' } else { '.' }
645 }
646
647 fn default_currency(&self) -> String {
648 numval_currency(&self.program.environment.currency)
649 }
650
651 fn read(&self, loc: Loc, pos: Pos) -> R<Val> {
652 store::read(&self.facts(), &self.unit.mem, loc, pos)
653 }
654
655 fn operand_with_loc(&mut self, op: &Operand, pos: Pos) -> R<(Val, Option<Loc>)> {
656 if let Operand::Ref(r) = op {
657 let loc = self.locate(r)?;
658 return Ok((self.read(loc, r.pos)?, Some(loc)));
659 }
660 Ok((self.operand(op, pos)?, None))
661 }
662
663 fn operand(&mut self, op: &Operand, pos: Pos) -> R<Val> {
664 match op {
665 Operand::Ref(r) => {
666 let loc = self.locate(r)?;
667 self.read(loc, r.pos)
668 }
669 Operand::Literal(lit) => self.literal_value(lit, pos),
670 Operand::LengthOf(r) => {
671 let loc = self.locate(r)?;
672 Ok(Val::Num(Fixed::new(loc.len as i128, Places::new(9, 0))))
673 }
674 Operand::Function(f) => self.function(f),
675 Operand::AddressOf(r) => Ok(Val::Address(self.address_of(r)?)),
676 }
677 }
678
679 fn set_integer(&mut self, r: &Ref, value: i64, pos: Pos) -> R<()> {
680 let dest = self.locate(r)?;
681 store::set_integer(&self.facts(), self.unit, dest, value, pos)
682 }
683
684 fn overflow_branch(&mut self, overflow: bool, on: &'p Option<Vec<Stmt>>, not_on: &'p Option<Vec<Stmt>>) -> R<Flow> {
685 match (overflow, on, not_on) {
686 (true, Some(body), _) | (false, _, Some(body)) => self.run_block(body),
687 _ => Ok(Flow::Next),
688 }
689 }
690
691 fn string_stmt(&mut self, st: &'p StringStmt) -> R<Flow> {
692 let sources: Vec<_> = st.sources.iter().map(|(op, d)| StringSource { chars: facts::chars(op), delimiter: match d {
693 Delimiter::Size => None,
694 Delimiter::By(d) => Some(facts::chars(d)),
695 } }).collect();
696 let overflow = rt::text::string(self, &st.into, st.pointer.as_ref(), &sources, st.pos)?;
697 self.overflow_branch(overflow, &st.on_overflow, &st.not_on_overflow)
698 }
699
700 fn unstring(&mut self, u: &'p Unstring) -> R<Flow> {
701 let delimiters: Vec<_> = u.delimiters.iter().map(|(all, d)| (*all, facts::chars(d))).collect();
702 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();
703 let overflow = rt::text::unstring(self, &u.source, u.pointer.as_ref(), &delimiters, &into, u.tallying.as_ref(), u.pos)?;
704 self.overflow_branch(overflow, &u.on_overflow, &u.not_on_overflow)
705 }
706
707 fn inspect(&mut self, i: &Inspect) -> R<()> {
708 let tallying: Vec<_> = i.tallying.iter().map(|p| self.inspect_phrase(p)).collect();
709 let Operand::Ref(target) = &i.target else {
710 return rt::text::tally(self, &&i.target, &tallying, i.pos);
711 };
712 let replacing: Vec<_> = i.replacing.iter().map(|p| self.inspect_phrase(p)).collect();
713 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) });
714 rt::text::inspect(self, target, &tallying, &replacing, converting.as_ref(), i.pos)
715 }
716
717 fn search(&mut self, se: &'p Search) -> R<Flow> {
718 let pos = se.pos;
719 let Resolved::Item(t) = self.resolve(&se.table)? else {
720 return Err(Abend::ironwork(format!("SEARCH {}: not a table", se.table.name), pos));
721 };
722 let layout = self.layout;
723 let table = &layout.items[t];
724 let count = self.occurrences(t, pos)? as i64;
725 let index = match (&se.varying, table.index_names.first()) {
726 (_, Some(name)) => Ref { name: name.clone(), qualifiers: Vec::new(), subscripts: Vec::new(), refmod: None, pos },
727 (Some(v), None) => v.clone(),
728 (None, None) => return Err(Abend::ironwork(format!("SEARCH {}: the table has no INDEXED BY", se.table.name), pos)),
729 };
730 let index_expr = Expr::Operand(Operand::Ref(index.clone()));
731 if !se.all {
732 loop {
733 let i = self.integer(&index_expr, pos)?;
734 if i < 1 || i > count {
735 return match &se.at_end {
736 Some(body) => self.run_block(body),
737 None => Ok(Flow::Next),
738 };
739 }
740 for (cond, body) in &se.whens {
741 if self.condition(cond, pos)? {
742 return self.run_block(body);
743 }
744 }
745 self.set_integer(&index, i + 1, pos)?;
746 if let Some(v) = &se.varying
747 && v.name != index.name
748 {
749 let current = self.integer(&Expr::Operand(Operand::Ref(v.clone())), pos)?;
750 self.set_integer(v, current + 1, pos)?;
751 }
752 }
753 }
754 let (cond, body) = &se.whens[0];
755 let mut terms = Vec::new();
756 flatten_and(cond, &mut terms);
757 let (mut low, mut high) = (1i64, count);
758 while low <= high {
759 let mid = (low + high) / 2;
760 self.set_integer(&index, mid, pos)?;
761 let mut order = Ordering::Equal;
762 for (ascending, key) in &table.keys {
763 let Some((subject, value)) = key_term(&terms, &key.name) else { continue };
764 let o = self.compare(subject, value, pos)?;
765 order = if *ascending { o } else { o.reverse() };
766 if order != Ordering::Equal {
767 break;
768 }
769 }
770 match order {
771 Ordering::Less => low = mid + 1,
772 Ordering::Greater => high = mid - 1,
773 Ordering::Equal => {
774 return if self.condition(cond, pos)? {
775 self.run_block(body)
776 } else {
777 match &se.at_end {
778 Some(b) => self.run_block(b),
779 None => Ok(Flow::Next),
780 }
781 };
782 }
783 }
784 }
785 match &se.at_end {
786 Some(b) => self.run_block(b),
787 None => Ok(Flow::Next),
788 }
789 }
790
791 fn address_of(&mut self, r: &Ref) -> R<u32> {
793 if let Ok(Resolved::Item(i)) = self.resolve(r)
794 && let Some(l) = self.layout.items[i].linkage
795 && self.linkage[l as usize].is_none()
796 {
797 return Ok(0);
798 }
799 let loc = self.locate(r)?;
800 Ok(ADDRESS_BASE + loc.offset as u32)
801 }
802
803 fn program_name(&mut self, op: &Operand, pos: Pos) -> R<String> {
804 Ok(match self.operand(op, pos)? {
805 Val::Bytes(b) => self.page.decode(&b).trim().to_ascii_uppercase(),
806 _ => return Err(Abend::ironwork("a program name must be alphanumeric", pos)),
807 })
808 }
809
810 pub(crate) fn sink(&mut self, kind: &'static str, pos: Pos, operand: &str) {
812 let source = self.unit.programs[self.me].source.clone();
813 let program = self.program;
814 let file = match (pos.file, &source) {
815 (0, Some(path)) => path.to_str().unwrap_or_default(),
816 (i, _) => program.sources.get(i as usize).map_or("", String::as_str),
817 };
818 self.unit.notify(Event::Sink { kind, file, line: pos.line, operand });
819 }
820
821 pub(crate) fn in_loaded(&self, index: usize, compiled: &Compiled, mut abend: Abend) -> Abend {
824 if abend.file.is_none()
825 && let Some(source) = &self.unit.programs[index].source
826 {
827 abend.file = Some(match abend.pos.file {
828 0 => source.display().to_string(),
829 i => compiled.program.sources.get(i as usize).cloned().unwrap_or_default(),
830 });
831 }
832 abend
833 }
834
835 fn arguments_text(&mut self, c: &Call) -> R<String> {
837 let mut text = String::new();
838 for op in c.using.iter().filter_map(|a| a.value.as_ref()) {
839 let bytes = match op {
840 Operand::Ref(r) => {
841 let loc = self.locate(r)?;
842 self.bytes(loc).to_vec()
843 }
844 _ => self.content_argument(op, c.pos)?,
845 };
846 text.push_str(&self.page.decode(&bytes));
847 }
848 Ok(text)
849 }
850
851 fn call(&mut self, c: &'p Call) -> R<Flow> {
852 if let Some(flow) = self.call_through_pointer(c)? {
853 return Ok(flow);
854 }
855 let pos = c.pos;
856 let name = self.program_name(&c.target, pos)?;
857 let variable = !matches!(c.target, Operand::Literal(_));
858 if self.unit.observed() {
859 if variable {
860 self.sink("dynamic-program-load", pos, &name);
861 }
862 if OS_COMMAND_ROUTINES.contains(&name.as_str())
864 && let Ok(text) = self.arguments_text(c)
865 {
866 self.sink("os-command", pos, &text);
867 }
868 }
869 let dynamic = self.options.dynam || variable;
870 let (index, entry) = match self.unit.load_entry(&name, dynamic) {
871 Ok(i) => i,
872 Err(LoadError::NotFound) if crate::le::provides(&name) => return self.le_call(c, &name),
873 Err(LoadError::NotFound) => {
874 return match &c.on_exception {
875 Some(body) => self.run_block(body),
876 None => Err(Abend { code: AbendCode::ModuleNotFound, message: crate::le::missing(&name), pos, file: None }),
877 };
878 }
879 Err(LoadError::Compile(message)) => return Err(Abend::ironwork(format!("CALL {name}: {message}"), pos)),
880 };
881 let Some(compiled) = self.unit.programs[index].compiled.clone() else {
882 return Err(Abend::ironwork(format!("CALL {name}: the first program of the run unit is already active"), pos));
883 };
884 if self.unit.programs[index].active && !compiled.program.recursive {
885 return Err(Abend::ironwork(format!("CALL {name}: the program is already active and is not RECURSIVE"), pos));
886 }
887 self.nest(pos)?;
888 let result = self.call_nested(c, index, entry, compiled);
889 self.unit.depth -= 1;
890 result
891 }
892
893 fn call_nested(&mut self, c: &'p Call, index: usize, entry: Option<usize>, compiled: std::rc::Rc<Compiled>) -> R<Flow> {
894 let pos = c.pos;
895 let mark = self.unit.mem.len();
896 let mut addresses = Vec::new();
897 for arg in &c.using {
898 let Some(op) = &arg.value else {
899 addresses.push(None);
900 continue;
901 };
902 let at = match (arg.mode, op) {
903 (ArgMode::Reference, Operand::Ref(r)) => {
904 let loc = self.locate(r)?;
905 loc.offset
906 }
907 (ArgMode::Value, _) => {
908 let bytes = self.value_argument(op, pos)?;
909 self.unit.push_temporary(&bytes)
910 }
911 (_, _) => {
912 let bytes = self.content_argument(op, pos)?;
913 self.unit.push_temporary(&bytes)
914 }
915 };
916 addresses.push(Some(at));
917 }
918 let outcome = {
919 let mut callee = Machine::activation(&compiled, index, &mut *self.unit, false)?;
920 let entry = entry.and_then(|k| compiled.entries.get(k));
921 callee.bind_using(entry.map_or(&compiled.program.using, |e| &e.using), &addresses);
922 callee.bind_returning();
923 let ending = callee.run_from(entry.map(|e| (e.paragraph, e.statement)));
924 let returned = match (&compiled.program.returning, &ending) {
925 (Some(item), Ok(_)) => Some(callee.returned(item, pos)?),
926 _ => None,
927 };
928 (ending, returned)
929 };
930 self.unit.programs[index].active = false;
931 if compiled.program.initial {
932 self.unit.programs[index].initialized = false;
933 }
934 self.unit.release_temporaries(mark);
935 let (ending, returned) = outcome;
936 let ending = ending.map_err(|a| self.in_loaded(index, &compiled, a));
937 if ending? == Ending::StopRun {
938 return Ok(Flow::End(Ending::StopRun));
939 }
940 if let (Some(target), Some(val)) = (&c.returning, returned) {
941 let dest = self.locate(target)?;
942 self.assign(dest, val, None, pos)?;
943 }
944 match &c.not_on_exception {
945 Some(body) => self.run_block(body),
946 None => Ok(Flow::Next),
947 }
948 }
949
950 fn bind(&mut self, addresses: &[Option<usize>]) {
952 let program = self.program;
953 self.bind_using(&program.using, addresses);
954 }
955
956 fn bind_using(&mut self, using: &[Param], addresses: &[Option<usize>]) {
959 for (param, address) in using.iter().zip(addresses) {
960 if let Some(ordinal) = self.layout.linkage_roots.iter().position(|&i| self.layout.items[i].name.as_deref() == Some(param.name.as_str())) {
961 self.linkage[ordinal] = *address;
962 }
963 }
964 }
965
966 fn bind_returning(&mut self) {
969 let Some(name) = &self.program.returning else { return };
970 let Some(ordinal) = self.layout.linkage_roots.iter().position(|&i| self.layout.items[i].name.as_deref() == Some(name.as_str())) else { return };
971 let size = self.layout.items[self.layout.linkage_roots[ordinal]].size as usize;
972 self.linkage[ordinal] = Some(self.unit.push_temporary(&vec![0; size]));
973 }
974
975 fn returned(&mut self, name: &str, pos: Pos) -> R<Val> {
976 let r = Ref { name: name.to_owned(), qualifiers: Vec::new(), subscripts: Vec::new(), refmod: None, pos };
977 let loc = self.locate(&r)?;
978 self.read(loc, pos)
979 }
980
981 fn content_argument(&mut self, op: &Operand, pos: Pos) -> R<Vec<u8>> {
983 if let Operand::Ref(r) = op {
984 let loc = self.locate(r)?;
985 return Ok(self.bytes(loc).to_vec());
986 }
987 Ok(match self.operand(op, pos)? {
988 Val::Bytes(b) | Val::All(b) | Val::National(b) => b,
989 Val::Fig(f) => vec![self.collating.figurative(f)],
990 Val::Address(a) => a.to_be_bytes().to_vec(),
991 Val::Num(f) if matches!(op, Operand::LengthOf(_)) => (align(&f, 0, false).and_then(|m| m.to_u128()).unwrap_or(0) as u32).to_be_bytes().to_vec(),
992 Val::Num(f) => {
993 let digits = f.places.total().max(1);
994 let magnitude = align(&f, f.places.dec, false).and_then(|m| m.to_u128()).unwrap_or(0);
995 zoned_digits(magnitude, digits as usize, if f.negative { decimal::MINUS } else { decimal::UNSIGNED })
996 }
997 Val::Float(h) => h.to_bytes(),
998 })
999 }
1000
1001 fn value_argument(&mut self, op: &Operand, pos: Pos) -> R<Vec<u8>> {
1003 Ok(match self.operand(op, pos)? {
1004 Val::Num(f) => {
1005 let whole = align(&f, 0, false).and_then(|m| m.to_u128()).and_then(|m| i32::try_from(m).ok()).ok_or_else(|| Abend::ironwork("a BY VALUE integer beyond a fullword", pos))?;
1006 (if f.negative { -whole } else { whole }).to_be_bytes().to_vec()
1007 }
1008 Val::Address(a) => a.to_be_bytes().to_vec(),
1009 Val::Fig(Figurative::Null) => vec![0; 4],
1010 Val::Bytes(b) => b,
1011 _ => return Err(Abend::ironwork("this BY VALUE argument is not supported", pos)),
1012 })
1013 }
1014
1015 fn cancel(&mut self, name: &str, pos: Pos) -> R<()> {
1016 let Some(index) = self.unit.find(name) else { return Ok(()) };
1017 if self.unit.programs[index].active {
1018 return Err(Abend::ironwork(format!("CANCEL {name}: the program is active"), pos));
1019 }
1020 let files: Vec<_> = self.unit.programs[index].files.iter_mut().filter_map(Option::take).collect();
1021 for f in files {
1022 f.close().map_err(|e| Abend::ironwork(format!("CANCEL {name}: {e}"), pos))?;
1023 }
1024 self.unit.programs[index].initialized = false;
1025 Ok(())
1026 }
1027
1028 fn set(&mut self, set: &SetStmt, pos: Pos) -> R<()> {
1029 match set {
1030 SetStmt::ConditionTrue(targets) | SetStmt::ConditionFalse(targets) => {
1031 let truth = matches!(set, SetStmt::ConditionTrue(_));
1032 for r in targets {
1033 let Resolved::Condition(index) = self.resolve(r)? else {
1034 return Err(Abend::ironwork(format!("SET {} TO {}: not a condition-name", r.name, if truth { "TRUE" } else { "FALSE" }), pos));
1035 };
1036 let condition = &self.layout.conditions[index];
1037 let value = if truth { condition.values.first().map(|(v, _)| v) } else { condition.false_value.as_ref() };
1038 let Some(value) = value else { continue };
1039 let dest = self.locate_item(condition.item, r, false)?;
1040 let val = self.literal_value(value, pos)?;
1041 self.assign(dest, val, None, pos)?;
1042 }
1043 }
1044 SetStmt::To { targets, value } => {
1045 for r in targets {
1046 let dest = self.locate(r)?;
1047 let (val, src) = self.operand_with_loc(value, pos)?;
1048 let (val, src) = rt::set::to(dest, val, src, pos)?;
1049 self.assign(dest, val, src, pos)?;
1050 }
1051 }
1052 SetStmt::AddressOf { targets, value } => {
1053 let val = self.operand(value, pos)?;
1054 let offset = rt::set::address(val, self.unit.mem.len(), pos)?;
1055 for r in targets {
1056 let Resolved::Item(i) = self.resolve(r)? else {
1057 return Err(Abend::ironwork(format!("SET ADDRESS OF {}: not a data item", r.name), pos));
1058 };
1059 let Some(ordinal) = self.layout.items[i].linkage.filter(|_| self.layout.items[i].parent.is_none()) else {
1060 return Err(Abend::ironwork(format!("SET ADDRESS OF {}: only a LINKAGE record can be given an address", r.name), pos));
1061 };
1062 self.linkage[ordinal as usize] = offset;
1063 }
1064 }
1065 SetStmt::UpDown { targets, down, by } => {
1066 let by = self.integer(by, pos)?;
1067 let targets: Vec<&Ref> = targets.iter().collect();
1068 rt::set::up_down(self, by, *down, &targets, pos)?;
1069 }
1070 }
1071 Ok(())
1072 }
1073
1074 fn accept(&mut self, target: &Ref, from: AcceptFrom, pos: Pos) -> R<()> {
1075 let dest = self.locate_receiving(target)?;
1076 rt::accept::accept(&self.facts(), self.unit, dest, from, &target.name, pos)
1077 }
1078
1079 fn function(&mut self, f: &FunctionCall) -> R<Val> {
1080 if let Some(value) = self.storage_function(f)? {
1081 return self.function_refmod(f, value);
1082 }
1083 let args = self.function_arguments(f)?;
1084 let side = match f.modifier.as_deref() {
1085 Some("LEADING") => Some(TrimSide::Leading),
1086 Some("TRAILING") => Some(TrimSide::Trailing),
1087 _ => None,
1088 };
1089 let value = rt::intrinsic::function::evaluate(&mut intrinsic::Call { machine: self, f }, &f.name, side, args, f.pos)?;
1090 self.function_refmod(f, value)
1091 }
1092
1093 fn function_refmod(&mut self, f: &FunctionCall, value: Val) -> R<Val> {
1094 let pos = f.pos;
1095 let Some(rm) = &f.refmod else { return Ok(value) };
1096 rt::intrinsic::function::refmod(value, pos, || {
1097 let start = self.integer(&rm.start, pos)?;
1098 let length = match &rm.length {
1099 Some(l) => Some(self.integer(l, pos)?),
1100 None => None,
1101 };
1102 Ok((start, length))
1103 })
1104 }
1105
1106 fn expr_value(&mut self, e: &Expr, pos: Pos) -> R<Val> {
1107 match e {
1108 Expr::Operand(op) => self.operand(op, pos),
1109 _ if self.uses_float(e)? => Ok(Val::Float(self.eval_float(e, self.options.arith.float_intermediate(), pos)?)),
1110 _ => {
1111 let dmax = self.dmax(e)?;
1112 Ok(Val::Num(self.eval_fixed(e, dmax, pos)?))
1113 }
1114 }
1115 }
1116
1117 fn operand_kind(&mut self, op: &Operand) -> R<Option<Kind>> {
1118 Ok(match op {
1119 Operand::Ref(r) => Some(self.locate(r)?.kind),
1120 _ => None,
1121 })
1122 }
1123
1124 fn uses_float(&mut self, e: &Expr) -> R<bool> {
1126 Ok(match e {
1127 Expr::Operand(Operand::Function(f)) => self.is_floating_point(f)?,
1128 Expr::Operand(op) => matches!(self.operand_kind(op)?, Some(Kind::Float(_))),
1129 Expr::Neg(inner) => self.uses_float(inner)?,
1130 Expr::Bin(a, _, b) => self.uses_float(a)? || self.uses_float(b)?,
1131 })
1132 }
1133
1134 fn dmax(&mut self, e: &Expr) -> R<u32> {
1137 Ok(match e {
1138 Expr::Operand(Operand::Literal(Literal::Number(t))) => literal_fixed(t).map_or(0, |f| f.places.dec),
1139 Expr::Operand(op) => self.operand_kind(op)?.and_then(Kind::digits_scale).map_or(0, |(_, s)| s),
1140 Expr::Neg(inner) => self.dmax(inner)?,
1141 Expr::Bin(a, BinOp::Div | BinOp::Pow, _) => self.dmax(a)?,
1142 Expr::Bin(a, _, b) => self.dmax(a)?.max(self.dmax(b)?),
1143 })
1144 }
1145
1146 fn eval_fixed(&mut self, e: &Expr, dmax: u32, pos: Pos) -> R<Fixed> {
1147 let arith = self.options.arith;
1148 match e {
1149 Expr::Operand(op) => {
1150 let val = self.operand(op, pos)?;
1151 arith::fixed_operand(val, dmax, pos)
1152 }
1153 Expr::Neg(inner) => Ok(arith::fixed_neg(self.eval_fixed(inner, dmax, pos)?)),
1154 Expr::Bin(a, op, b) => {
1155 let x = self.eval_fixed(a, dmax, pos)?;
1156 if *op == BinOp::Pow {
1157 let n = self.integer(b, pos)?;
1158 return arith::pow(x, n, dmax, arith, pos);
1159 }
1160 let y = self.eval_fixed(b, dmax, pos)?;
1161 if arith::divides_by_zero(*op, &y) {
1162 let binary = self.binary_division(a, b)?;
1163 return Err(arith::zero_divide(binary, pos));
1164 }
1165 arith::fixed_binop(x, *op, y, dmax, arith, pos)
1166 }
1167 }
1168 }
1169
1170 fn binary_division(&mut self, a: &Expr, b: &Expr) -> R<bool> {
1174 let mut items = 0;
1175 Ok(self.binary_operands(a, &mut items)? && self.binary_operands(b, &mut items)? && items > 0)
1176 }
1177
1178 fn binary_operands(&mut self, e: &Expr, items: &mut usize) -> R<bool> {
1179 Ok(match e {
1180 Expr::Operand(Operand::Literal(Literal::Number(t))) => !t.contains('.'),
1181 Expr::Operand(Operand::Literal(Literal::Figurative(Figurative::Zero))) => true,
1182 Expr::Operand(Operand::LengthOf(_)) => {
1183 *items += 1;
1184 true
1185 }
1186 Expr::Operand(op @ Operand::Ref(_)) => {
1187 let binary = matches!(self.operand_kind(op)?, Some(Kind::Binary { scale: 0, .. } | Kind::Index));
1188 *items += usize::from(binary);
1189 binary
1190 }
1191 Expr::Operand(_) => false,
1192 Expr::Neg(inner) => self.binary_operands(inner, items)?,
1193 Expr::Bin(x, _, y) => self.binary_operands(x, items)? && self.binary_operands(y, items)?,
1194 })
1195 }
1196
1197 fn eval_float(&mut self, e: &Expr, p: Precision, pos: Pos) -> R<Hfp> {
1198 match e {
1199 Expr::Operand(op) => {
1200 let val = self.operand(op, pos)?;
1201 arith::float_operand(val, p, pos)
1202 }
1203 Expr::Neg(inner) => Ok(arith::float_neg(self.eval_float(inner, p, pos)?)),
1204 Expr::Bin(a, op, b) => {
1205 let (x, y) = (self.eval_float(a, p, pos)?, self.eval_float(b, p, pos)?);
1206 arith::float_binop(x, *op, y, p, pos)
1207 }
1208 }
1209 }
1210
1211 fn arithmetic(&mut self, computations: &[(Target, Expr)], remainder: Option<&(Target, Expr, Expr)>, handler: Option<&'p SizeError>, per_receiver: bool, pos: Pos) -> R<Flow> {
1216 let mut size_error = false;
1217 let mut dmax = 0;
1218 let mut float_receiver = false;
1221 for (t, e) in computations {
1222 let loc = self.locate(&t.r)?;
1223 float_receiver |= matches!(loc.kind, Kind::Float(_));
1224 dmax = dmax.max(precision::receiver_dec(loc.kind.digits_scale().map_or(0, |(_, s)| s), t.rounded)).max(self.dmax(e)?);
1225 }
1226 if let Some((t, dividend, _)) = remainder {
1227 let loc = self.locate(&t.r)?;
1228 dmax = dmax.max(loc.kind.digits_scale().map_or(0, |(_, s)| s)).max(self.dmax(dividend)?);
1229 }
1230 let mut quotient_target: Option<Loc> = None;
1231 let mut results = Vec::with_capacity(computations.len());
1232 for (t, e) in computations {
1233 let own = |x: &Expr| per_receiver && matches!(x, Expr::Operand(Operand::Ref(r)) if *r == t.r);
1234 let float = float_receiver || self.uses_float(e)?;
1235 let (shared, with) = match e {
1236 Expr::Bin(a, op, b) if *op != BinOp::Pow && own(a) => (b.as_ref(), Some((*op, true))),
1237 Expr::Bin(a, op, b) if *op != BinOp::Pow && own(b) => (a.as_ref(), Some((*op, false))),
1238 _ => (e, None),
1239 };
1240 let outcome = if float {
1241 self.eval_float(shared, self.options.arith.float_intermediate(), pos).map(Val::Float)
1242 } else {
1243 self.eval_fixed(shared, dmax, pos).map(Val::Num)
1244 };
1245 results.push((t, shared, with, outcome));
1246 }
1247 let operands = match remainder {
1248 Some((_, dividend, divisor)) => Some((self.eval_fixed(dividend, dmax, pos)?, self.eval_fixed(divisor, dmax, pos)?)),
1249 None => None,
1250 };
1251 for (t, shared, with, outcome) in results {
1252 let loc = self.locate(&t.r)?;
1253 quotient_target.get_or_insert(loc);
1254 let outcome = match (with, outcome) {
1255 (Some((op, receiver_first)), Ok(Val::Num(value))) => {
1256 let current = self.eval_fixed(&Expr::Operand(Operand::Ref(t.r.clone())), dmax, pos)?;
1257 let (x, y) = if receiver_first { (current, value) } else { (value, current) };
1258 if arith::divides_by_zero(op, &y) {
1259 let receiver = Expr::Operand(Operand::Ref(t.r.clone()));
1260 let binary = self.binary_division(&receiver, shared)?;
1261 Err(arith::zero_divide(binary, pos))
1262 } else {
1263 arith::fixed_binop(x, op, y, dmax, self.options.arith, pos).map(Val::Num)
1264 }
1265 }
1266 (Some((op, receiver_first)), Ok(Val::Float(value))) => {
1267 let p = self.options.arith.float_intermediate();
1268 let current = self.eval_float(&Expr::Operand(Operand::Ref(t.r.clone())), p, pos)?;
1269 let (x, y) = if receiver_first { (current, value) } else { (value, current) };
1270 arith::float_binop(x, op, y, p, pos).map(Val::Float)
1271 }
1272 (_, outcome) => outcome,
1273 };
1274 let Some(value) = arith::size_error(outcome, handler.is_some())? else {
1275 size_error = true;
1276 continue;
1277 };
1278 size_error |= self.store_value(loc, value, t.rounded, handler.is_some(), pos)?;
1279 }
1280 if let (Some((t, _, _)), Some((x, y)), Some(q_loc)) = (remainder, operands, quotient_target)
1281 && let Some(r) = arith::remainder(x, y, places_of(q_loc.kind).dec, dmax, self.options.arith, pos)?
1282 {
1283 let r_loc = self.locate(&t.r)?;
1284 size_error |= self.store_value(r_loc, Val::Num(r), false, handler.is_some(), pos)?;
1285 }
1286 if let Some(h) = handler {
1287 return self.run_block(if size_error { &h.on } else { &h.not_on });
1288 }
1289 Ok(Flow::Next)
1290 }
1291
1292 fn store_value(&mut self, loc: Loc, value: Val, rounded: bool, keep_on_size_error: bool, pos: Pos) -> R<bool> {
1294 store::store_value(&self.facts(), self.unit, loc, value, rounded, keep_on_size_error, pos)
1295 }
1296
1297 fn store_fixed(&mut self, loc: Loc, value: &Fixed, rounded: bool, pos: Pos) -> R<()> {
1298 store::store_fixed(&self.facts(), self.unit, loc, value, rounded, pos)
1299 }
1300
1301 fn store_fixed_checked(&mut self, loc: Loc, value: &Fixed, rounded: bool, keep_on_size_error: bool, pos: Pos) -> R<bool> {
1302 store::store_fixed_checked(&self.facts(), self.unit, loc, value, rounded, keep_on_size_error, pos)
1303 }
1304
1305 fn assign(&mut self, dest: Loc, val: Val, src: Option<Loc>, pos: Pos) -> R<()> {
1307 store::assign(&self.facts(), self.unit, dest, val, src, pos)
1308 }
1309
1310 fn condition(&mut self, c: &Cond, pos: Pos) -> R<bool> {
1311 Ok(match c {
1312 Cond::Rel(a, op, b) => {
1313 let o = self.compare(a, b, pos)?;
1314 match op {
1315 RelOp::Eq => o == Ordering::Equal,
1316 RelOp::Ne => o != Ordering::Equal,
1317 RelOp::Lt => o == Ordering::Less,
1318 RelOp::Le => o != Ordering::Greater,
1319 RelOp::Gt => o == Ordering::Greater,
1320 RelOp::Ge => o != Ordering::Less,
1321 }
1322 }
1323 Cond::Not(inner) => !self.condition(inner, pos)?,
1324 Cond::And(a, b) => self.condition(a, pos)? && self.condition(b, pos)?,
1325 Cond::Or(a, b) => self.condition(a, pos)? || self.condition(b, pos)?,
1326 Cond::Class(e, class) => self.class(e, *class, pos)?,
1327 Cond::NameOrRel { subject, op, negated, name } => match self.resolve(name)? {
1328 Resolved::Condition(_) => self.condition(&Cond::Name(name.clone()), pos)?,
1329 Resolved::Item(_) => self.condition(&Cond::Rel(subject.clone(), *op, Expr::Operand(Operand::Ref(name.clone()))), pos)? != *negated,
1330 },
1331 Cond::Name(r) => {
1332 let Resolved::Condition(index) = self.resolve(r)? else {
1333 return Err(Abend::ironwork(format!("{} is a data item, not a condition", r.name), r.pos));
1334 };
1335 let condition = &self.layout.conditions[index];
1336 let loc = self.locate_item(condition.item, r, false)?;
1337 let subject = (self.read(loc, r.pos)?, Some(loc));
1338 for (low, high) in &condition.values {
1339 let hit = match high {
1340 None => self.compare_literal(&subject, low, pos)? == Ordering::Equal,
1341 Some(high) => self.compare_literal(&subject, low, pos)? != Ordering::Less && self.compare_literal(&subject, high, pos)? != Ordering::Greater,
1342 };
1343 if hit {
1344 return Ok(true);
1345 }
1346 }
1347 false
1348 }
1349 })
1350 }
1351
1352 fn class(&mut self, e: &Expr, class: Class, pos: Pos) -> R<bool> {
1353 if let (Class::Numeric | Class::Alphabetic, Expr::Operand(Operand::Ref(r))) = (class, e) {
1354 let loc = self.locate(r)?;
1355 let test = match (class, loc.kind) {
1356 (Class::Numeric, Kind::Packed { signed, .. }) => ByteClass::Packed { signed },
1357 (Class::Numeric, Kind::Zoned { signed, sign: None, .. }) => ByteClass::Zoned { signed },
1358 (Class::Numeric, _) => ByteClass::Digits,
1359 (_, _) => ByteClass::Alphabetic,
1360 };
1361 return Ok(store::byte_class(&self.facts(), &self.unit.mem, loc, test));
1362 }
1363 let test = match class {
1364 Class::Positive => SignTest::Positive,
1365 Class::Negative => SignTest::Negative,
1366 _ => SignTest::Zero,
1367 };
1368 store::sign_test(self.expr_value(e, pos)?, test, pos)
1369 }
1370
1371 fn compare(&mut self, a: &Expr, b: &Expr, pos: Pos) -> R<Ordering> {
1373 for (zoned, other, zoned_first) in [(a, b, true), (b, a, false)] {
1374 if let Some(image) = self.zoned_bytes_against(zoned, other)? {
1375 let other = match other {
1376 Expr::Operand(Operand::Ref(r)) if self.zone_sensitive(other)? => {
1377 let loc = self.locate(r)?;
1378 (Val::Bytes(Vec::new()), Some(loc))
1379 }
1380 _ => self.comparand(other, pos)?,
1381 };
1382 return store::compare_zoned_bytes(&self.facts(), &self.unit.mem, &image, other, zoned_first, pos);
1383 }
1384 }
1385 let (va, la) = self.comparand(a, pos)?;
1386 let (vb, lb) = self.comparand(b, pos)?;
1387 if let Some(o) = self.compare_references(a, b, (&va, la), (&vb, lb), pos)? {
1388 return Ok(o);
1389 }
1390 store::compare(&self.facts(), &self.unit.mem, (va, la), (vb, lb), pos)
1391 }
1392
1393 fn zoned_bytes_against(&mut self, e: &Expr, other: &Expr) -> R<Option<Vec<u8>>> {
1396 let Expr::Operand(Operand::Ref(r)) = e else { return Ok(None) };
1397 let nonnumeric = match other {
1398 Expr::Operand(Operand::Literal(l)) => matches!(l, Literal::Alnum(_) | Literal::Hex(_) | Literal::All(_))
1399 || matches!(l, Literal::Figurative(f) if !matches!(f, Figurative::Zero | Figurative::Null)),
1400 Expr::Operand(Operand::Ref(o)) => matches!(self.locate(o)?.kind, Kind::Group | Kind::Alnum { .. } | Kind::AlnumEdited { .. } | Kind::NumericEdited { .. }),
1401 _ => false,
1402 };
1403 let zones_count = self.options.invdata.is_some_and(|i| !i.forcenumcmp)
1406 && self.zone_sensitive(e)?
1407 && match other {
1408 Expr::Operand(Operand::Literal(Literal::Figurative(Figurative::Zero))) => true,
1409 Expr::Operand(Operand::Ref(o)) => self.zone_sensitive(other)? && self.locate(o)?.len == self.locate(r)?.len,
1410 _ => false,
1411 };
1412 if !nonnumeric && !zones_count {
1413 return Ok(None);
1414 }
1415 let loc = self.locate(r)?;
1416 Ok(store::compared_zoned_bytes(&self.facts(), &self.unit.mem, loc))
1417 }
1418
1419 fn zone_sensitive(&mut self, e: &Expr) -> R<bool> {
1421 let Expr::Operand(Operand::Ref(r)) = e else { return Ok(false) };
1422 let loc = self.locate(r)?;
1423 Ok(matches!(loc.kind, Kind::Zoned { scale: 0, signed: false, .. }) && self.layout.items.get(loc.item).is_none_or(|i| i.scaling == 0))
1424 }
1425
1426 fn compare_literal(&mut self, subject: &(Val, Option<Loc>), literal: &Literal, pos: Pos) -> R<Ordering> {
1427 let value = self.literal_value(literal, pos)?;
1428 store::compare(&self.facts(), &self.unit.mem, subject.clone(), (value, None), pos)
1429 }
1430
1431 fn comparand(&mut self, e: &Expr, pos: Pos) -> R<(Val, Option<Loc>)> {
1432 match e {
1433 Expr::Operand(op) => self.operand_with_loc(op, pos),
1434 _ => Ok((self.expr_value(e, pos)?, None)),
1435 }
1436 }
1437
1438 fn display(&mut self, items: &[Operand], no_advancing: bool, pos: Pos) -> R<()> {
1439 let mut text = String::new();
1440 for op in items {
1441 let shown = match op {
1442 Operand::Ref(r) => {
1443 let loc = self.locate(r)?;
1444 rt::display::place(&self.facts(), &self.unit.mem, loc, r.pos)?
1445 }
1446 Operand::Literal(Literal::Number(t)) => rt::display::number(t, &self.facts()),
1447 other => {
1448 let val = self.operand(other, pos)?;
1449 rt::display::value(&self.facts(), val, pos)?
1450 }
1451 };
1452 text.push_str(&shown);
1453 }
1454 if self.unit.observed() {
1455 self.sink("log", pos, &text);
1456 }
1457 rt::display::write(&mut *self.unit.out, &text, no_advancing, pos)
1458 }
1459
1460 fn initialize(&mut self, index: usize, offset: usize, pos: Pos) -> R<()> {
1461 let layout = self.layout;
1462 let item = &layout.items[index];
1463 if matches!(item.kind, Kind::Index | Kind::ObjectReference | Kind::ProgramPointer) {
1464 return Ok(());
1465 }
1466 if item.kind != Kind::Group {
1467 let loc = Loc { offset, len: item.size as usize, kind: item.kind, item: index };
1468 let val = match item.kind {
1469 Kind::Pointer => Val::Address(0),
1470 Kind::Alnum { .. } | Kind::National => Val::Fig(Figurative::Space),
1471 _ => Val::Fig(Figurative::Zero),
1472 };
1473 return self.assign(loc, val, None, pos);
1474 }
1475 for &c in &item.children {
1476 let child = &layout.items[c];
1477 if child.redefines.is_some() || child.name.is_none() {
1478 continue;
1479 }
1480 for k in 0..child.occurs {
1481 self.initialize(c, offset + (child.offset - item.offset) as usize + (k * child.size) as usize, pos)?;
1482 }
1483 }
1484 Ok(())
1485 }
1486}
1487
1488pub(crate) fn numval_currency(signs: &[CurrencySign]) -> String {
1490 match signs {
1491 [only] => only.value.clone(),
1492 _ => "$".to_owned(),
1493 }
1494}
1495
1496pub(crate) fn flatten_and<'c>(cond: &'c Cond, out: &mut Vec<&'c Cond>) {
1497 match cond {
1498 Cond::And(a, b) => {
1499 flatten_and(a, out);
1500 flatten_and(b, out);
1501 }
1502 other => out.push(other),
1503 }
1504}
1505
1506pub(crate) fn key_term<'c>(terms: &[&'c Cond], key: &str) -> Option<(&'c Expr, &'c Expr)> {
1508 let is_key = |e: &Expr| matches!(e, Expr::Operand(Operand::Ref(r)) if r.name == key);
1509 terms.iter().find_map(|t| match t {
1510 Cond::Rel(a, RelOp::Eq, b) if is_key(a) => Some((a, b)),
1511 Cond::Rel(a, RelOp::Eq, b) if is_key(b) => Some((b, a)),
1512 _ => None,
1513 })
1514}