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