Skip to main content

surfer_translation_types/
translator.rs

1//! Definition of the main [`Translator`] trait and the simplified version
2//! [`BasicTranslator`].
3#[cfg(feature = "wasm_plugins")]
4use extism_convert::{FromBytes, Json, ToBytes};
5use eyre::Result;
6use num::BigUint;
7use serde::{Deserialize, Serialize};
8use std::sync::mpsc::Sender;
9
10use std::borrow::Cow;
11
12use crate::result::TranslationResult;
13use crate::{
14    NAN_HIGHIMP, NAN_UNDEF, TranslationPreference, ValueKind, ValueRepr, VariableEncoding,
15    VariableInfo, VariableMeta, VariableValue, parse_numeric_string,
16};
17
18/// The numeric range that a translator can represent for a given variable.
19/// Used for Type Limits Y-axis scaling in analog waveform display.
20#[cfg_attr(feature = "wasm_plugins", derive(FromBytes, ToBytes))]
21#[cfg_attr(feature = "wasm_plugins", encoding(Json))]
22#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
23pub struct NumericRange {
24    pub min: f64,
25    pub max: f64,
26}
27
28#[cfg_attr(feature = "wasm_plugins", derive(FromBytes, ToBytes))]
29#[cfg_attr(feature = "wasm_plugins", encoding(Json))]
30#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
31pub enum TrueName {
32    /// The variable's true name is best represented as part of a line of code
33    /// for example if line 100 is
34    /// let x = a + b;
35    /// and the signal being queried is `a+b` then this would return
36    /// {line: 100, before: "let x = ", this: "a + b", after: ";"}
37    SourceCode {
38        line_number: usize,
39        before: String,
40        this: String,
41        after: String,
42    },
43}
44
45/// Provides a way for translators to "change" the name of variables in the variable list.
46/// Most translators should not produce `VariableNameInfo` since it is a global thing that
47/// is done on _all_ variables, not just those which have had the translator applied.
48///
49/// An example use case is translators for HDLs which want to translate from automatically
50/// generated subexpression back into names that a human can understand. In this use case,
51/// it is _very_ unlikely that the user wants to see the raw anonymous name that the compiler
52/// emitted, so performing this translation globally makes sense.
53#[cfg_attr(feature = "wasm_plugins", derive(FromBytes, ToBytes))]
54#[cfg_attr(feature = "wasm_plugins", encoding(Json))]
55#[derive(Clone, Debug, Serialize, Deserialize)]
56pub struct VariableNameInfo {
57    /// A more human-undesrstandable name for a signal.
58    ///
59    /// This should only be used by translators which understand the context of the variable and
60    /// can produce a better name than the raw name.
61    pub true_name: Option<TrueName>,
62    /// Translators can change the order that signals appear in the variable list using this
63    /// parameter. Before rendering, the variable will be sported by this number in descending
64    /// order, so variables that are predicted to be extra important to the
65    /// user should have a number > 0 while unimportant variables should be < 0
66    ///
67    /// Translators should only poke at this variable if they know something about the variable.
68    /// For example, an HDL translator that does not recognise a name should leave it at None
69    /// to give other translators the chance to set the priority
70    pub priority: Option<i32>,
71}
72
73#[cfg_attr(feature = "wasm_plugins", derive(FromBytes, ToBytes))]
74#[cfg_attr(feature = "wasm_plugins", encoding(Json))]
75#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
76pub enum WaveSource {
77    File(String),
78    Data,
79    DragAndDrop(Option<String>),
80    Url(String),
81    Cxxrtl,
82}
83
84/// The most general translator trait.
85pub trait Translator<VarId, ScopeId, Message>: Send + Sync {
86    /// Name of the translator to be shown in the UI
87    fn name(&self) -> String;
88
89    /// Notify the translator that the wave source has changed to the specified source
90    fn set_wave_source(&self, _wave_source: Option<WaveSource>) {}
91
92    /// Translate the specified variable value into a human-readable form
93    fn translate(
94        &self,
95        variable: &VariableMeta<VarId, ScopeId>,
96        value: &VariableValue,
97    ) -> Result<TranslationResult>;
98
99    /// Return information about the structure of a variable, see [`VariableInfo`].
100    fn variable_info(&self, variable: &VariableMeta<VarId, ScopeId>) -> Result<VariableInfo>;
101
102    /// Return [`TranslationPreference`] based on if the translator can handle this variable.
103    fn translates(&self, variable: &VariableMeta<VarId, ScopeId>) -> Result<TranslationPreference>;
104
105    /// Translate a variable value to a numeric f64 for analog rendering.
106    ///
107    /// Returns [`NAN_UNDEF`] for undefined values and [`NAN_HIGHIMP`] for high-impedance.
108    /// The default implementation calls [`Self::translate`] and parses the result.
109    /// Translators that produce numeric output should override this for
110    /// efficient analog signal rendering without string round-trip.
111    fn translate_numeric(
112        &self,
113        variable: &VariableMeta<VarId, ScopeId>,
114        value: &VariableValue,
115    ) -> Option<f64> {
116        let translation = self.translate(variable, value).ok()?;
117
118        // Check ValueKind first - if it's HighImp or Undef, return appropriate NaN
119        if matches!(translation.kind, ValueKind::HighImp) {
120            return Some(NAN_HIGHIMP);
121        }
122        if matches!(translation.kind, ValueKind::Undef) {
123            return Some(NAN_UNDEF);
124        }
125
126        // Try to parse as numeric value
127        let value_str: Cow<str> = match &translation.val {
128            ValueRepr::Bit(c) => Cow::Owned(c.to_string()),
129            ValueRepr::Bits(_, s) => Cow::Borrowed(s),
130            ValueRepr::String(s) => Cow::Borrowed(s),
131            _ => return None,
132        };
133        parse_numeric_string(&value_str, &self.name())
134    }
135
136    /// By default translators are stateless, but if they need to reload, they can
137    /// do by defining this method.
138    /// Long running translators should run the reloading in the background using `perform_work`
139    fn reload(&self, _sender: Sender<Message>) {}
140
141    /// Returns a [`VariableNameInfo`] about the specified variable which will be applied globally.
142    /// Most translators should simply return `None` here, see the
143    /// documentation [`VariableNameInfo`] for exceptions to this rule.
144    fn variable_name_info(
145        &self,
146        variable: &VariableMeta<VarId, ScopeId>,
147    ) -> Option<VariableNameInfo> {
148        // We could name this `_variable`, but that means the docs will make it look unused
149        // and LSPs will fill in the definition with that name too, so we'll mark it as unused
150        // like this
151        let _ = variable;
152        None
153    }
154
155    fn numeric_range(&self, variable: &VariableMeta<VarId, ScopeId>) -> Option<NumericRange> {
156        let _ = variable;
157        None
158    }
159}
160
161/// A translator that only produces non-hierarchical values
162pub trait BasicTranslator<VarId, ScopeId>: Send + Sync {
163    /// Name of the translator to be shown in the UI
164    fn name(&self) -> String;
165
166    /// Translate the specified variable value into a human-readable form.
167    ///
168    /// If the translator require [`VariableMeta`] information to perform the translation,
169    /// use the more general [`Translator`] instead.
170    fn basic_translate(&self, num_bits: u32, value: &VariableValue) -> (String, ValueKind);
171
172    /// Translate a variable value to a numeric f64 for analog rendering.
173    ///
174    /// Returns [`NAN_UNDEF`] for undefined values and [`NAN_HIGHIMP`] for high-impedance.
175    /// The default implementation calls [`Self::basic_translate`] and parses the result.
176    /// Translators that produce numeric output should override this for
177    /// efficient analog signal rendering without string round-trip.
178    fn basic_translate_numeric(&self, num_bits: u32, value: &VariableValue) -> Option<f64> {
179        let (val, kind) = self.basic_translate(num_bits, value);
180
181        // Check ValueKind first - if it's HighImp or Undef, return appropriate NaN
182        match kind {
183            ValueKind::HighImp => return Some(NAN_HIGHIMP),
184            ValueKind::Undef => return Some(NAN_UNDEF),
185            _ => {}
186        }
187
188        parse_numeric_string(&val, &self.name())
189    }
190
191    /// Return [`TranslationPreference`] based on if the translator can handle this variable.
192    ///
193    /// If this is not implemented, it will default to accepting all bit-vector types.
194    fn translates(&self, variable: &VariableMeta<VarId, ScopeId>) -> Result<TranslationPreference> {
195        translates_all_bit_types(variable)
196    }
197
198    /// Return information about the structure of a variable, see [`VariableInfo`].
199    ///
200    /// If this is not implemented, it will default to [`VariableInfo::Bits`].
201    fn variable_info(&self, _variable: &VariableMeta<VarId, ScopeId>) -> Result<VariableInfo> {
202        Ok(VariableInfo::Bits)
203    }
204
205    fn basic_numeric_range(&self, _num_bits: u32) -> Option<NumericRange> {
206        None
207    }
208}
209
210enum NumberParseResult {
211    Numerical(BigUint),
212    Unparsable(String, ValueKind),
213}
214
215/// Turn vector variable string into name and corresponding kind if it
216/// includes values other than 0 and 1. If only 0 and 1, return None.
217fn map_vector_variable(s: &str) -> NumberParseResult {
218    if let Some(val) = BigUint::parse_bytes(s.as_bytes(), 2) {
219        NumberParseResult::Numerical(val)
220    } else if s.contains('x') {
221        NumberParseResult::Unparsable("UNDEF".to_string(), ValueKind::Undef)
222    } else if s.contains('z') {
223        NumberParseResult::Unparsable("HIGHIMP".to_string(), ValueKind::HighImp)
224    } else if s.contains('-') {
225        NumberParseResult::Unparsable("DON'T CARE".to_string(), ValueKind::DontCare)
226    } else if s.contains('u') {
227        NumberParseResult::Unparsable("UNDEF".to_string(), ValueKind::Undef)
228    } else if s.contains('w') {
229        NumberParseResult::Unparsable("UNDEF WEAK".to_string(), ValueKind::Undef)
230    } else if s.contains('h') || s.contains('l') {
231        NumberParseResult::Unparsable("WEAK".to_string(), ValueKind::Weak)
232    } else {
233        NumberParseResult::Unparsable("UNKNOWN VALUES".to_string(), ValueKind::Undef)
234    }
235}
236
237impl VariableValue {
238    /// Parse into a [`BigUint`], returning an error with `ValueKind` for X/Z values.
239    ///
240    /// Returns `Cow::Borrowed` for `BigUint` values and `Cow::Owned` for parsed strings.
241    pub fn parse_biguint(&self) -> Result<Cow<'_, BigUint>, (String, ValueKind)> {
242        match self {
243            VariableValue::BigUint(v) => Ok(Cow::Borrowed(v)),
244            VariableValue::String(s) => match map_vector_variable(s) {
245                NumberParseResult::Unparsable(v, k) => Err((v, k)),
246                NumberParseResult::Numerical(v) => Ok(Cow::Owned(v)),
247            },
248        }
249    }
250}
251
252/// A helper function for translators that translates all bit vector types.
253pub fn translates_all_bit_types<VarId, ScopeId>(
254    variable: &VariableMeta<VarId, ScopeId>,
255) -> Result<TranslationPreference> {
256    if variable.encoding == VariableEncoding::BitVector {
257        Ok(TranslationPreference::Yes)
258    } else {
259        Ok(TranslationPreference::No)
260    }
261}