rd_ast/raw.rs
1//! Lossless fallback types for Rd structures that don't fit the
2//! [`RdNode::Tagged`](crate::RdNode::Tagged) or
3//! [`RdNode::Group`](crate::RdNode::Group) shapes.
4//!
5//! Raw's normative boundary and consumer treatment are in the crate's
6//! included `CONTRACT.md`.
7
8/// A losslessly-preserved Rd node that doesn't fit
9/// [`RdNode::Tagged`](crate::RdNode::Tagged) or
10/// [`RdNode::Group`](crate::RdNode::Group): it carries attributes beyond
11/// those the producer recognizes (for the RDS lowering, beyond
12/// `Rd_tag`/`Rd_option` plus the deliberately-discarded `srcref` -- see that
13/// module's validated attribute policy), or has another genuinely
14/// non-canonical shape (an untagged non-list value, or an untagged node
15/// carrying an `Rd_option`).
16#[derive(Debug, Clone, PartialEq)]
17#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
18pub struct RawRdNode {
19 /// The node's `Rd_tag` string, if any. `None` for an untagged
20 /// non-canonical node; ordinary positional list groups are represented by
21 /// [`crate::RdNode::Group`]. `Some(text)` preserves a
22 /// recognized-shape-but-unexpected node's original tag text exactly
23 /// (this is a different mechanism from
24 /// [`RdTag::Unknown`](crate::RdTag::Unknown), which lives inside a
25 /// well-formed [`RdNode::Tagged`](crate::RdNode::Tagged) node -- `Raw`
26 /// is for nodes whose *shape*, not just tag spelling, was unexpected).
27 tag: Option<String>,
28 /// The bracket-option content (`Rd_option`), if present.
29 option: Option<Vec<crate::RdNode>>,
30 /// The node's children.
31 children: Vec<crate::RdNode>,
32 /// The original node value when it cannot be represented as Rd children.
33 ///
34 /// `None` means the value is represented by `children`; `Some` preserves
35 /// an opaque value that has no child representation. A correct producer
36 /// never encodes the same value in both non-empty `children` and `payload`.
37 payload: Option<Box<RawRdValue>>,
38 /// Any attributes beyond `Rd_tag`/`Rd_option`, preserved verbatim.
39 attributes: Vec<RdAttribute>,
40}
41
42impl RawRdNode {
43 pub fn tag(&self) -> Option<&str> {
44 self.tag.as_deref()
45 }
46 pub fn option(&self) -> Option<&[crate::RdNode]> {
47 self.option.as_deref()
48 }
49 pub fn children(&self) -> &[crate::RdNode] {
50 &self.children
51 }
52 pub fn payload(&self) -> Option<&RawRdValue> {
53 self.payload.as_deref()
54 }
55 pub fn attributes(&self) -> &[RdAttribute] {
56 &self.attributes
57 }
58 /// Consumes the node into `(tag, option, children, payload, attributes)`.
59 #[allow(clippy::type_complexity)]
60 pub fn into_parts(
61 self,
62 ) -> (
63 Option<String>,
64 Option<Vec<crate::RdNode>>,
65 Vec<crate::RdNode>,
66 Option<RawRdValue>,
67 Vec<RdAttribute>,
68 ) {
69 (
70 self.tag,
71 self.option,
72 self.children,
73 self.payload.map(|payload| *payload),
74 self.attributes,
75 )
76 }
77}
78
79/// A single R-level attribute attached to a raw Rd node, preserved for
80/// losslessness.
81#[derive(Debug, Clone, PartialEq)]
82#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
83pub struct RdAttribute {
84 /// The attribute's name.
85 name: String,
86 /// The attribute's value, including attributes attached to that value.
87 value: RawRdObject,
88}
89
90impl RdAttribute {
91 pub fn name(&self) -> &str {
92 &self.name
93 }
94 pub fn value(&self) -> &RawRdObject {
95 &self.value
96 }
97 pub fn into_parts(self) -> (String, RawRdObject) {
98 (self.name, self.value)
99 }
100}
101
102/// A recursively preserved object used by raw attribute values and lists.
103///
104/// Keeping the object's attributes alongside its value is important for
105/// structures such as `srcref`: the integer vector itself carries a nested
106/// `srcfile` attribute. This producer-agnostic representation retains that
107/// shape without exposing an RDS decoder type from `rd-ast`.
108#[derive(Debug, Clone, PartialEq)]
109#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
110pub struct RawRdObject {
111 /// The object's value.
112 value: RawRdValue,
113 /// Attributes attached to this value, recursively preserved.
114 attributes: Vec<RdAttribute>,
115}
116
117impl RawRdObject {
118 pub fn value(&self) -> &RawRdValue {
119 &self.value
120 }
121 pub fn attributes(&self) -> &[RdAttribute] {
122 &self.attributes
123 }
124 pub fn into_parts(self) -> (RawRdValue, Vec<RdAttribute>) {
125 (self.value, self.attributes)
126 }
127}
128
129/// Advanced, lossless construction surface for AST producers.
130/// Normal consumers should inspect Raw values rather than synthesize them.
131pub mod producer {
132 use super::{RawRdNode, RawRdObject, RawRdValue, RdAttribute};
133 use crate::RdNode;
134
135 pub fn raw_node(
136 tag: Option<String>,
137 option: Option<Vec<RdNode>>,
138 children: Vec<RdNode>,
139 payload: Option<RawRdValue>,
140 attributes: Vec<RdAttribute>,
141 ) -> RawRdNode {
142 RawRdNode {
143 tag,
144 option,
145 children,
146 payload: payload.map(Box::new),
147 attributes,
148 }
149 }
150 pub fn raw_attribute(name: String, value: RawRdObject) -> RdAttribute {
151 RdAttribute { name, value }
152 }
153 pub fn raw_object(value: RawRdValue, attributes: Vec<RdAttribute>) -> RawRdObject {
154 RawRdObject { value, attributes }
155 }
156}
157
158/// The value of a recursively preserved raw object.
159#[derive(Debug, Clone, PartialEq)]
160#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
161#[non_exhaustive]
162pub enum RawRdValue {
163 /// R's `NULL` value.
164 Null,
165 /// An integer vector; `None` entries represent `NA`.
166 Integer(Vec<Option<i32>>),
167 /// A logical vector; `None` entries represent `NA`.
168 Logical(Vec<Option<bool>>),
169 /// A real vector with explicit representations for R's special values.
170 Real(Vec<RawRdReal>),
171 /// A character vector; `None` entries represent `NA`.
172 Character(Vec<Option<String>>),
173 /// A list whose elements retain their own attributes.
174 List(Vec<RawRdObject>),
175 /// A symbol name.
176 Symbol(String),
177 /// The string payload of a persisted reference.
178 Persisted(Vec<Option<String>>),
179 /// An opaque environment handle.
180 Environment(RawRdEnvironment),
181}
182
183/// The environment categories exposed by a producer when preserving a raw
184/// value. Environment frames are intentionally not part of the Rd AST.
185#[derive(Debug, Clone, Copy, PartialEq, Eq)]
186#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
187#[non_exhaustive]
188pub enum RawRdEnvironment {
189 Global,
190 Base,
191 Empty,
192 Other,
193}
194
195/// A single value in an R real vector.
196///
197/// The special values use dedicated variants so serde formats such as JSON
198/// never collapse `NA`, `NaN`, and positive or negative infinity into the
199/// same `null` representation. This guarantee depends on
200/// [`Finite`](RawRdReal::Finite) itself only ever holding an actual finite
201/// payload -- see its documentation for the invariant.
202///
203/// # Why the invariant is documented rather than enforced
204///
205/// Nothing at the type level stops a caller from constructing
206/// `Finite(f64::NAN)` or `Finite(f64::INFINITY)`. Leaving that unenforced
207/// is a deliberate trade-off:
208///
209/// * Within this crate the only producer is the RDS lowering
210/// (`lower_raw_real` in `rds.rs`), which classifies every incoming value
211/// (`NA` / `NaN` / positive or negative infinity / finite) into the
212/// matching variant *before* construction, so lowering can never produce
213/// a non-finite `Finite`.
214/// * Empirically, real (double) vectors do not occur in real-world R help
215/// databases at all: Rd trees consist of character data plus
216/// string/integer/logical attributes (`Rd_tag`, `Rd_option`, `srcref`,
217/// `Rdfile`, `class`, `dynamicFlag`, `macro`). A full scan of an
218/// installed corpus (46 packages, 3457 topics, covering both values and
219/// attributes) found zero double vectors. This type exists for
220/// completeness of the lossless raw mirror, not because such data is
221/// expected in practice.
222/// * For JSON specifically, the deserialize direction is already safe:
223/// JSON numbers cannot express `NaN` or infinities, and
224/// `{"kind":"finite","value":null}` fails to deserialize as `f64`. The
225/// unenforced direction is serialization of a hand-constructed
226/// non-finite `Finite` (silently emitted as `null`), and round-tripping
227/// through binary serde formats that can represent non-finite floats.
228///
229/// Given the above, enforcement (a validated newtype payload plus a
230/// custom `Deserialize`) was deliberately deferred; if these raw types
231/// ever become a supported input surface for external producers, that is
232/// the intended hardening path.
233#[derive(Debug, Clone, Copy, PartialEq)]
234#[cfg_attr(
235 feature = "serde",
236 derive(serde::Serialize, serde::Deserialize),
237 serde(tag = "kind", content = "value", rename_all = "snake_case")
238)]
239pub enum RawRdReal {
240 /// R's `NA_real_` value.
241 Na,
242 /// A finite IEEE-754 value.
243 ///
244 /// This variant must only hold a finite payload; `NA`, `NaN`, and the
245 /// two infinities have their own dedicated variants ([`Na`](RawRdReal::Na),
246 /// [`Nan`](RawRdReal::Nan), [`PositiveInfinity`](RawRdReal::PositiveInfinity),
247 /// [`NegativeInfinity`](RawRdReal::NegativeInfinity)). Constructing
248 /// `Finite` with a non-finite value violates this contract: nothing
249 /// prevents it at the type level, but the derived serde `Serialize`
250 /// impl would then encode it as JSON `null` (`serde_json`'s standard
251 /// treatment of non-finite floats), silently defeating the point of
252 /// keeping these variants separate. See the type-level documentation
253 /// for why this invariant is documented rather than enforced.
254 Finite(f64),
255 /// A non-`NA` IEEE-754 not-a-number value.
256 Nan,
257 /// Positive infinity.
258 PositiveInfinity,
259 /// Negative infinity.
260 NegativeInfinity,
261}