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