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