Skip to main content

pcode_types/expression/
types.rs

1use crate::{BinaryOperator, Expression, PcodeResolver, SpaceId, UnaryOperator};
2use serde::{Deserialize, Serialize};
3
4/// A space reference that may be unresolved at Phase 2 parse time.
5#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
6pub enum SpaceRef {
7    /// A space of the compiled specification. Always this variant by the time
8    /// a consumer sees an expression.
9    Resolved(SpaceId),
10    /// Space name absent from the symbol table during Phase 2; resolved in Phase 3.
11    Deferred(Box<str>),
12}
13
14impl SpaceRef {
15    /// The space this refers to.
16    ///
17    /// # Panics
18    ///
19    /// Panics on [`SpaceRef::Deferred`], which cannot occur in an expression
20    /// handed to a consumer — compilation resolves every space name or fails.
21    pub fn resolved(&self) -> SpaceId {
22        match self {
23            SpaceRef::Resolved(id) => *id,
24            SpaceRef::Deferred(name) => panic!("unresolved space `{name}` reached runtime"),
25        }
26    }
27}
28
29/// A bit position or width in a [`Range`], which a macro may parameterise.
30#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
31pub enum RangeParam {
32    /// A constant number of bits.
33    Literal(usize),
34
35    /// A macro parameter standing in for one. Substituted when the macro is
36    /// expanded, so a consumer does not see this variant.
37    MacroArg(crate::LocalVarId),
38}
39
40/// `x[start, size]` — `size` bits of `x`, counting from bit `start`.
41///
42/// Bit 0 is the least significant. The result is the smallest whole number of
43/// bytes that holds `size` bits.
44#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
45pub struct Range<S = ()> {
46    /// The value being sliced.
47    pub value: Box<Expression<S>>,
48    /// Index of the lowest bit taken, counting from the least significant.
49    pub start: RangeParam,
50    /// How many bits to take.
51    pub size: RangeParam,
52}
53
54impl<S> Range<S> {
55    /// Discards source spans.
56    pub fn strip_span(self) -> Range<()> {
57        Range {
58            value: Box::new(self.value.strip_span()),
59            start: self.start,
60            size: self.size,
61        }
62    }
63}
64
65impl Range {
66    /// Renders this range in a diagnostic-oriented SLEIGH-like syntax.
67    pub fn pretty_print(&self, spec: &impl PcodeResolver) -> String {
68        format!(
69            "range({}, {}, {})",
70            self.value.pretty_print(spec),
71            pretty_print_range_param(&self.start),
72            pretty_print_range_param(&self.size)
73        )
74    }
75}
76
77/// `*[space]:size ptr` — a read from memory.
78///
79/// A load from the constant space is not a memory access at all: it is the
80/// pointer value itself, taken at the declared width.
81#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
82pub struct Load<S = ()> {
83    /// Which space to read, or `None` for the producer's default space.
84    pub space: Option<SpaceRef>,
85
86    /// How many bytes to read, or `None` when the width is not written and
87    /// must come from the context the load appears in.
88    pub size: Option<usize>,
89
90    /// The address to read from.
91    pub ptr: Box<Expression<S>>,
92}
93
94impl<S> Load<S> {
95    /// Discards source spans.
96    pub fn strip_span(self) -> Load<()> {
97        Load {
98            space: self.space,
99            size: self.size,
100            ptr: Box::new(self.ptr.strip_span()),
101        }
102    }
103}
104
105impl Load {
106    /// Renders this load in a diagnostic-oriented SLEIGH-like syntax.
107    pub fn pretty_print(&self, spec: &impl PcodeResolver) -> String {
108        let mut parts = Vec::new();
109        if let Some(space) = &self.space {
110            let name = match space {
111                SpaceRef::Resolved(id) => pretty_print_space(spec, *id),
112                SpaceRef::Deferred(name) => format!("?{name}"),
113            };
114            parts.push(format!("space={name}"));
115        }
116        if let Some(size) = self.size {
117            parts.push(format!("size={size}"));
118        }
119        parts.push(format!("ptr={}", self.ptr.pretty_print(spec)));
120        format!("load({})", parts.join(", "))
121    }
122}
123
124/// A prefix operator and its operand.
125#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
126pub struct Unop<S = ()> {
127    /// Which operator.
128    pub op: UnaryOperator,
129    /// The operand.
130    pub e: Box<Expression<S>>,
131}
132
133impl<S> Unop<S> {
134    /// Discards source spans.
135    pub fn strip_span(self) -> Unop<()> {
136        Unop {
137            op: self.op,
138            e: Box::new(self.e.strip_span()),
139        }
140    }
141}
142
143impl Unop {
144    /// Renders this unary expression in a diagnostic-oriented SLEIGH-like syntax.
145    pub fn pretty_print(&self, spec: &impl PcodeResolver) -> String {
146        let expr = self.e.pretty_print(spec);
147        match self.op {
148            UnaryOperator::LogicalNot => format!("!{expr}"),
149            UnaryOperator::BitwiseNot => format!("~{expr}"),
150            UnaryOperator::Minus => format!("-{expr}"),
151            UnaryOperator::FloatMinus => format!("f-{expr}"),
152            UnaryOperator::AddressOf(Some(size)) => format!("&:{size} {expr}"),
153            UnaryOperator::AddressOf(None) => format!("&{expr}"),
154        }
155    }
156}
157
158/// An infix operator and its two operands.
159#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
160pub struct Binop<S = ()> {
161    /// Which operator.
162    pub op: BinaryOperator,
163    /// Left operand.
164    pub lhs: Box<Expression<S>>,
165    /// Right operand.
166    pub rhs: Box<Expression<S>>,
167}
168
169impl<S> Binop<S> {
170    /// Discards source spans.
171    pub fn strip_span(self) -> Binop<()> {
172        Binop {
173            op: self.op,
174            lhs: Box::new(self.lhs.strip_span()),
175            rhs: Box::new(self.rhs.strip_span()),
176        }
177    }
178}
179
180impl Binop {
181    /// Renders this binary expression in a diagnostic-oriented SLEIGH-like syntax.
182    pub fn pretty_print(&self, spec: &impl PcodeResolver) -> String {
183        format!(
184            "({} {} {})",
185            self.lhs.pretty_print(spec),
186            self.op.pretty_print(),
187            self.rhs.pretty_print(spec)
188        )
189    }
190}
191
192fn pretty_print_range_param(param: &RangeParam) -> String {
193    match param {
194        RangeParam::Literal(value) => value.to_string(),
195        RangeParam::MacroArg(id) => format!("arg{}", id.0),
196    }
197}
198
199fn pretty_print_space(spec: &impl PcodeResolver, id: SpaceId) -> String {
200    spec.space_name(id)
201}