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