Skip to main content

ironwork_rt/
storage.rs

1//! The storage vocabulary a program's data shares: how an item is held, where it sits, and the
2//! values that move between items.
3
4use crate::vocab::{Figurative, SignClause};
5use numeric::precision::{Fixed, Places};
6use zarch::hfp::{Hfp, Precision};
7use zarch::wide::U256;
8
9#[derive(Clone, Copy, Debug, PartialEq, Eq)]
10pub enum Kind {
11    Group,
12    Alnum { justified: bool },
13    National,
14    /// USAGE DISPLAY-1: DBCS characters, two bytes each, X'4040' the space. `edit` indexes the
15    /// layout's edited PICTUREs for one with B, a position that always holds a DBCS space.
16    Dbcs { justified: bool, edit: Option<u32> },
17    Zoned { digits: u32, scale: u32, signed: bool, sign: Option<SignClause> },
18    Packed { digits: u32, scale: u32, signed: bool },
19    Binary { digits: u32, scale: u32, signed: bool, native: bool },
20    Float(Precision),
21    /// `edit` indexes the layout's list of edited PICTUREs.
22    NumericEdited { edit: u32, digits: u32, scale: u32, blank_when_zero: bool },
23    AlnumEdited { edit: u32 },
24    /// USAGE POINTER: an address, four bytes.
25    Pointer,
26    /// An index name or USAGE INDEX item, holding an occurrence number in four bytes.
27    Index,
28    /// USAGE OBJECT REFERENCE: four bytes naming an object, as under LP(32).
29    ObjectReference,
30    /// USAGE FUNCTION-POINTER or PROCEDURE-POINTER, four bytes.
31    ProgramPointer,
32}
33
34impl Kind {
35    pub fn is_numeric(self) -> bool {
36        matches!(self, Kind::Zoned { .. } | Kind::Packed { .. } | Kind::Binary { .. } | Kind::Float(_) | Kind::Index)
37    }
38
39    /// Digits and decimal places of a fixed-point numeric item.
40    pub fn digits_scale(self) -> Option<(u32, u32)> {
41        match self {
42            Kind::Zoned { digits, scale, .. }
43            | Kind::Packed { digits, scale, .. }
44            | Kind::Binary { digits, scale, .. }
45            | Kind::NumericEdited { digits, scale, .. } => Some((digits, scale)),
46            Kind::Index => Some((9, 0)),
47            _ => None,
48        }
49    }
50}
51
52#[derive(Clone, Copy, Debug)]
53pub struct Loc {
54    pub offset: usize,
55    pub len: usize,
56    pub kind: Kind,
57    pub item: usize,
58}
59
60#[derive(Clone, Debug)]
61pub enum Val {
62    Bytes(Vec<u8>),
63    National(Vec<u8>),
64    /// DBCS characters, two bytes each, as a DBCS literal or item holds them.
65    Dbcs(Vec<u8>),
66    Num(Fixed),
67    Float(Hfp),
68    Fig(Figurative),
69    All(Vec<u8>),
70    /// ALL with a national literal: its UTF-16 units, repeated to the length of what it meets.
71    AllNational(Vec<u8>),
72    /// A pointer value: `ADDRESS_BASE` (in the interpreter's run unit) plus an offset into run-unit memory, or 0 for NULL.
73    Address(u32),
74}
75
76/// A numeric literal's value; None unless it holds 1 to 31 digits.
77pub fn literal_fixed(text: &str) -> Option<Fixed> {
78    let (negative, body) = match text.as_bytes().first() {
79        Some(b'-') => (true, &text[1..]),
80        Some(b'+') => (false, &text[1..]),
81        _ => (false, text),
82    };
83    let (int, frac) = body.split_once('.').unwrap_or((body, ""));
84    let digits = format!("{int}{frac}");
85    if digits.is_empty() || digits.len() > 31 {
86        return None;
87    }
88    let magnitude = U256::from_u128(digits.parse().ok()?);
89    Some(Fixed { negative: negative && !magnitude.is_zero(), magnitude, places: Places::new(int.len().max(1) as u32, frac.len() as u32) })
90}