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 crate::result::TranslationResult;
11use crate::{
12    TranslationPreference, ValueKind, VariableEncoding, VariableInfo, VariableMeta, VariableValue,
13};
14
15#[cfg_attr(feature = "wasm_plugins", derive(FromBytes, ToBytes))]
16#[cfg_attr(feature = "wasm_plugins", encoding(Json))]
17#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
18pub enum TrueName {
19    /// The variable's true name is best represented as part of a line of code
20    /// for example if line 100 is
21    /// let x = a + b;
22    /// and the signal being queried is `a+b` then this would return
23    /// {line: 100, before: "let x = ", this: "a + b", after: ";"}
24    SourceCode {
25        line_number: usize,
26        before: String,
27        this: String,
28        after: String,
29    },
30}
31
32/// Provides a way for translators to "change" the name of variables in the variable list.
33/// Most translators should not produce VariableNameInfo since it is a global thing that
34/// is done on _all_ variables, not just those which have had the translator applied.
35///
36/// An example use case is translators for HDLs which want to translate from automatically
37/// generated subexpression back into names that a human can understand. In this use case,
38/// it is _very_ unlikely that the user wants to see the raw anonymous name that the compiler
39/// emitted, so performing this translation globally makes sense.
40#[cfg_attr(feature = "wasm_plugins", derive(FromBytes, ToBytes))]
41#[cfg_attr(feature = "wasm_plugins", encoding(Json))]
42#[derive(Clone, Debug, Serialize, Deserialize)]
43pub struct VariableNameInfo {
44    /// A more human-undesrstandable name for a signal. This should only be used by translators
45    /// which
46    pub true_name: Option<TrueName>,
47    /// Translators can change the order that signals appear in the variable list using this
48    /// parameter. Before rendering, the variable will be sported by this number in descending
49    /// order, so variables that are predicted to be extra important to the
50    /// user should have a number > 0 while unimportant variables should be < 0
51    ///
52    /// Translators should only poke at this variable if they know something about the variable.
53    /// For example, an HDL translator that does not recognise a name should leave it at None
54    /// to give other translators the chance to set the priority
55    pub priority: Option<i32>,
56}
57
58#[cfg_attr(feature = "wasm_plugins", derive(FromBytes, ToBytes))]
59#[cfg_attr(feature = "wasm_plugins", encoding(Json))]
60#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
61pub enum WaveSource {
62    File(String),
63    Data,
64    DragAndDrop(Option<String>),
65    Url(String),
66    Cxxrtl,
67}
68
69/// The most general translator trait.
70pub trait Translator<VarId, ScopeId, Message>: Send + Sync {
71    fn name(&self) -> String;
72
73    /// Notify the translator that the wave source has changed to the specified source
74    fn set_wave_source(&self, _wave_source: Option<WaveSource>) {}
75
76    fn translate(
77        &self,
78        variable: &VariableMeta<VarId, ScopeId>,
79        value: &VariableValue,
80    ) -> Result<TranslationResult>;
81
82    fn variable_info(&self, variable: &VariableMeta<VarId, ScopeId>) -> Result<VariableInfo>;
83
84    /// Return [`TranslationPreference`] based on if the translator can handle this variable.
85    fn translates(&self, variable: &VariableMeta<VarId, ScopeId>) -> Result<TranslationPreference>;
86
87    /// By default translators are stateless, but if they need to reload, they can
88    /// do by defining this method.
89    /// Long running translators should run the reloading in the background using `perform_work`
90    fn reload(&self, _sender: Sender<Message>) {}
91
92    /// Returns a `VariableNameInfo` about the specified variable which will be applied globally.
93    /// Most translators should simply return `None` here, see the
94    /// documentation `VariableNameInfo` for exceptions to this rule.
95    fn variable_name_info(
96        &self,
97        variable: &VariableMeta<VarId, ScopeId>,
98    ) -> Option<VariableNameInfo> {
99        // We could name this `_variable`, but that means the docs will make it look unused
100        // and LSPs will fill in the definition with that name too, so we'll mark it as unused
101        // like this
102        let _ = variable;
103        None
104    }
105}
106
107/// A translator that only produces non-hierarchical values
108pub trait BasicTranslator<VarId, ScopeId>: Send + Sync {
109    fn name(&self) -> String;
110
111    fn basic_translate(&self, num_bits: u64, value: &VariableValue) -> (String, ValueKind);
112
113    fn translates(&self, variable: &VariableMeta<VarId, ScopeId>) -> Result<TranslationPreference> {
114        translates_all_bit_types(variable)
115    }
116
117    fn variable_info(&self, _variable: &VariableMeta<VarId, ScopeId>) -> Result<VariableInfo> {
118        Ok(VariableInfo::Bits)
119    }
120}
121
122enum NumberParseResult {
123    Numerical(BigUint),
124    Unparsable(String, ValueKind),
125}
126
127/// Turn vector variable string into name and corresponding kind if it
128/// includes values other than 0 and 1. If only 0 and 1, return None.
129fn map_vector_variable(s: &str) -> NumberParseResult {
130    if let Some(val) = BigUint::parse_bytes(s.as_bytes(), 2) {
131        NumberParseResult::Numerical(val)
132    } else if s.contains('x') {
133        NumberParseResult::Unparsable("UNDEF".to_string(), ValueKind::Undef)
134    } else if s.contains('z') {
135        NumberParseResult::Unparsable("HIGHIMP".to_string(), ValueKind::HighImp)
136    } else if s.contains('-') {
137        NumberParseResult::Unparsable("DON'T CARE".to_string(), ValueKind::DontCare)
138    } else if s.contains('u') {
139        NumberParseResult::Unparsable("UNDEF".to_string(), ValueKind::Undef)
140    } else if s.contains('w') {
141        NumberParseResult::Unparsable("UNDEF WEAK".to_string(), ValueKind::Undef)
142    } else if s.contains('h') || s.contains('l') {
143        NumberParseResult::Unparsable("WEAK".to_string(), ValueKind::Weak)
144    } else {
145        NumberParseResult::Unparsable("UNKNOWN VALUES".to_string(), ValueKind::Undef)
146    }
147}
148
149impl VariableValue {
150    pub fn parse_biguint(self) -> Result<BigUint, (String, ValueKind)> {
151        match self {
152            VariableValue::BigUint(v) => Ok(v),
153            VariableValue::String(s) => match map_vector_variable(&s) {
154                NumberParseResult::Unparsable(v, k) => Err((v, k)),
155                NumberParseResult::Numerical(v) => Ok(v),
156            },
157        }
158    }
159}
160
161pub fn translates_all_bit_types<VarId, ScopeId>(
162    variable: &VariableMeta<VarId, ScopeId>,
163) -> Result<TranslationPreference> {
164    if variable.encoding == VariableEncoding::BitVector {
165        Ok(TranslationPreference::Yes)
166    } else {
167        Ok(TranslationPreference::No)
168    }
169}