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