1use std::collections::HashMap;
2#[cfg(not(target_arch = "wasm32"))]
3use std::path::Path;
4use std::sync::mpsc::Sender;
5
6use color_eyre::Result;
7#[cfg(not(target_arch = "wasm32"))]
8use directories::ProjectDirs;
9use ecolor::Color32;
10#[cfg(not(target_arch = "wasm32"))]
11use log::warn;
12#[cfg(not(target_arch = "wasm32"))]
13use toml::Table;
14
15mod basic_translators;
16pub mod clock;
17mod enum_translator;
18mod fixed_point;
19mod instruction_translators;
20pub mod numeric_translators;
21#[cfg(feature = "python")]
22mod python_translators;
23#[cfg(not(target_arch = "wasm32"))]
24pub mod wasm_translator;
25
26pub use basic_translators::*;
27use clock::ClockTranslator;
28#[cfg(not(target_arch = "wasm32"))]
29use instruction_decoder::Decoder;
30pub use instruction_translators::*;
31use itertools::Itertools;
32pub use numeric_translators::*;
33use surfer_translation_types::{
34 BasicTranslator, HierFormatResult, SubFieldFlatTranslationResult, TranslatedValue,
35 TranslationPreference, TranslationResult, Translator, ValueKind, ValueRepr, VariableEncoding,
36 VariableInfo, VariableValue,
37};
38
39use crate::config::SurferTheme;
40use crate::translation::enum_translator::EnumTranslator;
41use crate::wave_container::{ScopeId, VarId};
42use crate::{message::Message, wave_container::VariableMeta};
43
44pub type DynTranslator = dyn Translator<VarId, ScopeId, Message>;
45pub type DynBasicTranslator = dyn BasicTranslator<VarId, ScopeId>;
46
47fn translate_with_basic(
48 t: &DynBasicTranslator,
49 variable: &VariableMeta,
50 value: &VariableValue,
51) -> Result<TranslationResult> {
52 let (val, kind) = t.basic_translate(variable.num_bits.unwrap_or(0) as u64, value);
53 Ok(TranslationResult {
54 val: ValueRepr::String(val),
55 kind,
56 subfields: vec![],
57 })
58}
59
60pub enum AnyTranslator {
61 Full(Box<DynTranslator>),
62 Basic(Box<DynBasicTranslator>),
63 #[cfg(feature = "python")]
64 Python(python_translators::PythonTranslator),
65}
66
67impl AnyTranslator {
68 pub fn is_basic(&self) -> bool {
69 matches!(self, AnyTranslator::Basic(_))
70 }
71}
72
73impl Translator<VarId, ScopeId, Message> for AnyTranslator {
74 fn name(&self) -> String {
75 match self {
76 AnyTranslator::Full(t) => t.name(),
77 AnyTranslator::Basic(t) => t.name(),
78 #[cfg(feature = "python")]
79 AnyTranslator::Python(t) => t.name(),
80 }
81 }
82
83 fn set_wave_source(&self, wave_source: Option<surfer_translation_types::WaveSource>) {
84 match self {
85 AnyTranslator::Full(translator) => translator.set_wave_source(wave_source),
86 AnyTranslator::Basic(_) => {}
87 #[cfg(feature = "python")]
88 AnyTranslator::Python(t) => {}
89 }
90 }
91
92 fn translate(
93 &self,
94 variable: &VariableMeta,
95 value: &VariableValue,
96 ) -> Result<TranslationResult> {
97 match self {
98 AnyTranslator::Full(t) => t.translate(variable, value),
99 AnyTranslator::Basic(t) => translate_with_basic(&**t, variable, value),
100 #[cfg(feature = "python")]
101 AnyTranslator::Python(t) => translate_with_basic(t, variable, value),
102 }
103 }
104
105 fn variable_info(&self, variable: &VariableMeta) -> Result<VariableInfo> {
106 match self {
107 AnyTranslator::Full(t) => t.variable_info(variable),
108 AnyTranslator::Basic(t) => t.variable_info(variable),
109 #[cfg(feature = "python")]
110 #[cfg(target_family = "unix")]
111 AnyTranslator::Python(t) => t.variable_info(variable),
112 }
113 }
114
115 fn translates(&self, variable: &VariableMeta) -> Result<TranslationPreference> {
116 match self {
117 AnyTranslator::Full(t) => t.translates(variable),
118 AnyTranslator::Basic(t) => t.translates(variable),
119 #[cfg(feature = "python")]
120 AnyTranslator::Python(t) => t.translates(variable),
121 }
122 }
123
124 fn reload(&self, sender: Sender<Message>) {
125 match self {
126 AnyTranslator::Full(t) => t.reload(sender),
127 AnyTranslator::Basic(_) => (),
128 #[cfg(feature = "python")]
129 AnyTranslator::Python(_) => (),
130 }
131 }
132
133 fn variable_name_info(
134 &self,
135 variable: &surfer_translation_types::VariableMeta<VarId, ScopeId>,
136 ) -> Option<surfer_translation_types::translator::VariableNameInfo> {
137 match self {
138 AnyTranslator::Full(translator) => translator.variable_name_info(variable),
139 AnyTranslator::Basic(_) => None,
140 #[cfg(feature = "python")]
141 AnyTranslator::Python(t) => None,
142 }
143 }
144}
145
146#[cfg(not(target_arch = "wasm32"))]
151fn find_user_decoders() -> Vec<Box<DynBasicTranslator>> {
152 let mut decoders: Vec<Box<DynBasicTranslator>> = vec![];
153 if let Some(proj_dirs) = ProjectDirs::from("org", "surfer-project", "surfer") {
154 let mut config_decoders = find_user_decoders_at_path(proj_dirs.config_dir());
155 decoders.append(&mut config_decoders);
156 }
157
158 let mut project_decoders = find_user_decoders_at_path(Path::new(".surfer"));
159 decoders.append(&mut project_decoders);
160
161 decoders
162}
163
164#[cfg(not(target_arch = "wasm32"))]
166fn find_user_decoders_at_path(path: &Path) -> Vec<Box<DynBasicTranslator>> {
167 use log::error;
168
169 let mut decoders: Vec<Box<DynBasicTranslator>> = vec![];
170 let Ok(decoder_dirs) = std::fs::read_dir(path.join("decoders")) else {
171 return decoders;
172 };
173
174 for decoder_dir in decoder_dirs.flatten() {
175 if decoder_dir.metadata().is_ok_and(|m| m.is_dir()) {
176 let Ok(name) = decoder_dir.file_name().into_string() else {
177 warn!("Cannot load decoder. Invalid name.");
178 continue;
179 };
180 let mut tomls = vec![];
181 let mut width: Option<toml::Value> = None;
184
185 if let Ok(toml_files) = std::fs::read_dir(decoder_dir.path()) {
186 for toml_file in toml_files.flatten() {
187 if toml_file
188 .file_name()
189 .into_string()
190 .is_ok_and(|file_name| file_name.ends_with(".toml"))
191 {
192 let Ok(text) = std::fs::read_to_string(toml_file.path()) else {
193 warn!(
194 "Skipping toml file {:?}. Cannot read file.",
195 toml_file.path()
196 );
197 continue;
198 };
199
200 let Ok(toml_parsed) = text.parse::<Table>() else {
201 warn!(
202 "Skipping toml file {:?}. Cannot parse toml.",
203 toml_file.path()
204 );
205 continue;
206 };
207
208 let Some(toml_width) = toml_parsed.get("width") else {
209 warn!(
210 "Skipping toml file {:?}. Mandatory key 'width' is missing.",
211 toml_file.path()
212 );
213 continue;
214 };
215
216 if width.clone().is_some_and(|width| width != *toml_width) {
217 warn!(
218 "Skipping toml file {:?}. Bit widths do not match.",
219 toml_file.path()
220 );
221 continue;
222 } else {
223 width = Some(toml_width.clone());
224 }
225
226 tomls.push(toml_parsed);
227 }
228 }
229 }
230
231 if let Some(width) = width.and_then(|width| width.as_integer()) {
232 match Decoder::new_from_table(tomls) {
233 Ok(decoder) => decoders.push(Box::new(InstructionTranslator {
234 name,
235 decoder,
236 num_bits: width.unsigned_abs(),
237 })),
238 Err(e) => {
239 error!("Error while building decoder {name}");
240 for toml in e {
241 for error in toml {
242 error!("{error}");
243 }
244 }
245 }
246 }
247 }
248 }
249 }
250 decoders
251}
252
253pub fn all_translators() -> TranslatorList {
254 #[allow(unused_mut)]
256 let mut basic_translators: Vec<Box<DynBasicTranslator>> = vec![
257 Box::new(BitTranslator {}),
258 Box::new(HexTranslator {}),
259 Box::new(OctalTranslator {}),
260 Box::new(GroupingBinaryTranslator {}),
261 Box::new(BinaryTranslator {}),
262 Box::new(ASCIITranslator {}),
263 Box::new(new_rv32_translator()),
264 Box::new(new_rv64_translator()),
265 Box::new(new_mips_translator()),
266 Box::new(new_la64_translator()),
267 Box::new(LebTranslator {}),
268 Box::new(UnsignedTranslator {}),
269 Box::new(SignedTranslator {}),
270 Box::new(SinglePrecisionTranslator {}),
271 Box::new(DoublePrecisionTranslator {}),
272 Box::new(HalfPrecisionTranslator {}),
273 Box::new(BFloat16Translator {}),
274 Box::new(Posit32Translator {}),
275 Box::new(Posit16Translator {}),
276 Box::new(Posit8Translator {}),
277 Box::new(PositQuire8Translator {}),
278 Box::new(PositQuire16Translator {}),
279 Box::new(E5M2Translator {}),
280 Box::new(E4M3Translator {}),
281 Box::new(NumberOfOnesTranslator {}),
282 Box::new(LeadingOnesTranslator {}),
283 Box::new(TrailingOnesTranslator {}),
284 Box::new(LeadingZerosTranslator {}),
285 Box::new(TrailingZerosTranslator {}),
286 Box::new(IdenticalMSBsTranslator {}),
287 #[cfg(feature = "f128")]
288 Box::new(QuadPrecisionTranslator {}),
289 ];
290
291 #[cfg(not(target_arch = "wasm32"))]
292 basic_translators.append(&mut find_user_decoders());
293
294 TranslatorList::new(
295 basic_translators,
296 vec![
297 Box::new(ClockTranslator::new()),
298 Box::new(StringTranslator {}),
299 Box::new(EnumTranslator {}),
300 Box::new(UnsignedFixedPointTranslator),
301 Box::new(SignedFixedPointTranslator),
302 ],
303 )
304}
305
306#[derive(Default)]
307pub struct TranslatorList {
308 inner: HashMap<String, AnyTranslator>,
309 #[cfg(feature = "python")]
310 python_translator: Option<(camino::Utf8PathBuf, String, AnyTranslator)>,
311 pub default: String,
312}
313
314impl TranslatorList {
315 pub fn new(basic: Vec<Box<DynBasicTranslator>>, translators: Vec<Box<DynTranslator>>) -> Self {
316 Self {
317 default: "Hexadecimal".to_string(),
318 inner: basic
319 .into_iter()
320 .map(|t| (t.name(), AnyTranslator::Basic(t)))
321 .chain(
322 translators
323 .into_iter()
324 .map(|t| (t.name(), AnyTranslator::Full(t))),
325 )
326 .collect(),
327 #[cfg(feature = "python")]
328 python_translator: None,
329 }
330 }
331
332 pub fn all_translator_names(&self) -> Vec<&str> {
333 #[cfg(feature = "python")]
334 let python_name = self
335 .python_translator
336 .as_ref()
337 .map(|(_, name, _)| name.as_str());
338 #[cfg(not(feature = "python"))]
339 let python_name = None;
340 self.inner
341 .keys()
342 .map(String::as_str)
343 .chain(python_name)
344 .collect()
345 }
346
347 pub fn all_translators(&self) -> Vec<&AnyTranslator> {
348 #[cfg(feature = "python")]
349 let python_translator = self.python_translator.as_ref().map(|(_, _, t)| t);
350 #[cfg(not(feature = "python"))]
351 let python_translator = None;
352 self.inner.values().chain(python_translator).collect()
353 }
354
355 pub fn basic_translator_names(&self) -> Vec<&str> {
356 self.inner
357 .iter()
358 .filter_map(|(name, t)| t.is_basic().then_some(name.as_str()))
359 .collect()
360 }
361
362 pub fn get_translator(&self, name: &str) -> &AnyTranslator {
363 #[cfg(feature = "python")]
364 let python_translator = || {
365 self.python_translator
366 .as_ref()
367 .filter(|(_, python_name, _)| python_name == name)
368 .map(|(_, _, t)| t)
369 };
370 #[cfg(not(feature = "python"))]
371 let python_translator = || None;
372 self.inner
373 .get(name)
374 .or_else(python_translator)
375 .unwrap_or_else(|| panic!("No translator called {name}"))
376 }
377
378 pub fn add_or_replace(&mut self, t: AnyTranslator) {
379 self.inner.insert(t.name(), t);
380 }
381
382 pub fn is_valid_translator(&self, meta: &VariableMeta, candidate: &str) -> bool {
383 self.get_translator(candidate)
384 .translates(meta)
385 .map(|preference| preference != TranslationPreference::No)
386 .unwrap_or(false)
387 }
388
389 #[cfg(feature = "python")]
390 pub fn load_python_translator(&mut self, filename: camino::Utf8PathBuf) -> Result<()> {
391 log::debug!("Reading Python code from disk: {filename}");
392 let code = std::ffi::CString::new(std::fs::read_to_string(&filename)?)?;
393 let mut translators = python_translators::PythonTranslator::new(&code.as_c_str())?;
394 if translators.len() != 1 {
395 color_eyre::eyre::bail!("Only one Python translator per file is supported for now");
396 }
397 let translator = translators.pop().unwrap();
398 self.python_translator = Some((
399 filename,
400 translator.name(),
401 AnyTranslator::Python(translator),
402 ));
403 Ok(())
404 }
405
406 #[cfg(feature = "python")]
407 pub fn has_python_translator(&self) -> bool {
408 self.python_translator.is_some()
409 }
410
411 #[cfg(feature = "python")]
412 pub fn reload_python_translator(&mut self) -> Result<()> {
413 if let Some((path, _, _)) = self.python_translator.take() {
414 self.load_python_translator(path)?;
415 }
416 Ok(())
417 }
418}
419
420fn format(
421 val: &ValueRepr,
422 kind: ValueKind,
423 subtranslator_name: &String,
424 translators: &TranslatorList,
425 subresults: &[HierFormatResult],
426) -> Option<TranslatedValue> {
427 match val {
428 ValueRepr::Bit(val) => {
429 let AnyTranslator::Basic(subtranslator) =
430 translators.get_translator(subtranslator_name)
431 else {
432 panic!("Subtranslator '{subtranslator_name}' was not a basic translator");
433 };
434
435 Some(TranslatedValue::from_basic_translate(
436 subtranslator.basic_translate(1, &VariableValue::String(val.to_string())),
437 ))
438 }
439 ValueRepr::Bits(bit_count, bits) => {
440 let AnyTranslator::Basic(subtranslator) =
441 translators.get_translator(subtranslator_name)
442 else {
443 panic!("Subtranslator '{subtranslator_name}' was not a basic translator");
444 };
445
446 Some(TranslatedValue::from_basic_translate(
447 subtranslator.basic_translate(*bit_count, &VariableValue::String(bits.clone())),
448 ))
449 }
450 ValueRepr::String(sval) => Some(TranslatedValue {
451 value: sval.clone(),
452 kind,
453 }),
454 ValueRepr::Tuple => Some(TranslatedValue {
455 value: format!(
456 "({})",
457 subresults
458 .iter()
459 .map(|v| v.this.as_ref().map_or("-", |t| t.value.as_str()))
460 .join(", ")
461 ),
462 kind,
463 }),
464 ValueRepr::Struct => Some(TranslatedValue {
465 value: format!(
466 "{{{}}}",
467 subresults
468 .iter()
469 .map(|v| {
470 let n = v.names.join("_");
471 format!("{n}: {}", v.this.as_ref().map_or("-", |t| t.value.as_str()))
472 })
473 .join(", ")
474 ),
475 kind,
476 }),
477 ValueRepr::Array => Some(TranslatedValue {
478 value: format!(
479 "[{}]",
480 subresults
481 .iter()
482 .map(|v| v.this.as_ref().map_or("-", |t| t.value.as_str()))
483 .join(", ")
484 ),
485 kind,
486 }),
487 ValueRepr::NotPresent => None,
488 ValueRepr::Enum { idx, name } => Some(TranslatedValue {
489 value: format!(
490 "{name}{{{}}}",
491 subresults[*idx]
492 .this
493 .as_ref()
494 .map_or("-", |t| t.value.as_str())
495 ),
496 kind,
497 }),
498 }
499}
500
501#[local_impl::local_impl]
502impl TranslationResultExt for TranslationResult {
503 fn sub_format(
504 &self,
505 formats: &[crate::displayed_item::FieldFormat],
506 translators: &TranslatorList,
507 path_so_far: &[String],
508 ) -> Vec<HierFormatResult> {
509 self.subfields
510 .iter()
511 .map(|res| {
512 let sub_path = path_so_far
513 .iter()
514 .chain([&res.name])
515 .cloned()
516 .collect::<Vec<_>>();
517
518 let sub = res.result.sub_format(formats, translators, &sub_path);
519
520 let translator_name = formats
523 .iter()
524 .find(|e| e.field == sub_path)
525 .map(|e| e.format.clone())
526 .unwrap_or(translators.default.clone());
527 let formatted = format(
528 &res.result.val,
529 res.result.kind,
530 &translator_name,
531 translators,
532 &sub,
533 );
534
535 HierFormatResult {
536 this: formatted,
537 names: sub_path,
538 fields: sub,
539 }
540 })
541 .collect::<Vec<_>>()
542 }
543
544 fn format_flat(
546 &self,
547 root_format: &Option<String>,
548 formats: &[crate::displayed_item::FieldFormat],
549 translators: &TranslatorList,
550 ) -> Vec<SubFieldFlatTranslationResult> {
551 let sub_result = self.sub_format(formats, translators, &[]);
552
553 let formatted = format(
557 &self.val,
558 self.kind,
559 root_format.as_ref().unwrap_or(&translators.default),
560 translators,
561 &sub_result,
562 );
563
564 let formatted = HierFormatResult {
565 names: vec![],
566 this: formatted,
567 fields: sub_result,
568 };
569 let mut collected = vec![];
570 formatted.collect_into(&mut collected);
571 collected
572 }
573}
574
575#[local_impl::local_impl]
576impl VariableInfoExt for VariableInfo {
577 fn get_subinfo(&self, path: &[String]) -> &VariableInfo {
578 match path {
579 [] => self,
580 [field, rest @ ..] => match self {
581 VariableInfo::Compound { subfields } => subfields
582 .iter()
583 .find(|(f, _)| f == field)
584 .unwrap()
585 .1
586 .get_subinfo(rest),
587 VariableInfo::Bits => panic!(),
588 VariableInfo::Bool => panic!(),
589 VariableInfo::Clock => panic!(),
590 VariableInfo::String => panic!(),
591 VariableInfo::Real => panic!(),
592 },
593 }
594 }
595
596 fn has_subpath(&self, path: &[String]) -> bool {
597 match path {
598 [] => true,
599 [field, rest @ ..] => match self {
600 VariableInfo::Compound { subfields } => subfields
601 .iter()
602 .find(|&(f, _)| f == field)
603 .is_some_and(|(_, info)| info.has_subpath(rest)),
604 _ => false,
605 },
606 }
607 }
608}
609
610#[local_impl::local_impl]
611impl ValueKindExt for ValueKind {
612 fn color(&self, user_color: Color32, theme: &SurferTheme) -> Color32 {
613 match self {
614 ValueKind::HighImp => theme.variable_highimp,
615 ValueKind::Undef => theme.variable_undef,
616 ValueKind::DontCare => theme.variable_dontcare,
617 ValueKind::Warn => theme.variable_undef,
618 ValueKind::Custom(custom_color) => *custom_color,
619 ValueKind::Weak => theme.variable_weak,
620 ValueKind::Normal => user_color,
621 }
622 }
623}
624
625pub struct StringTranslator {}
626
627impl Translator<VarId, ScopeId, Message> for StringTranslator {
628 fn name(&self) -> String {
629 "String".to_string()
630 }
631
632 fn translate(
633 &self,
634 _variable: &VariableMeta,
635 value: &VariableValue,
636 ) -> Result<TranslationResult> {
637 match value {
638 VariableValue::BigUint(b) => Ok(TranslationResult {
639 val: ValueRepr::String(format!("ERROR (0x{b:x})")),
640 kind: ValueKind::Warn,
641 subfields: vec![],
642 }),
643 VariableValue::String(s) => Ok(TranslationResult {
644 val: ValueRepr::String((*s).to_string()),
645 kind: ValueKind::Normal,
646 subfields: vec![],
647 }),
648 }
649 }
650
651 fn variable_info(&self, _variable: &VariableMeta) -> Result<VariableInfo> {
652 Ok(VariableInfo::String)
653 }
654
655 fn translates(&self, variable: &VariableMeta) -> Result<TranslationPreference> {
656 if variable.encoding == VariableEncoding::String
658 || variable.encoding == VariableEncoding::Real
659 {
660 Ok(TranslationPreference::Prefer)
661 } else {
662 Ok(TranslationPreference::No)
663 }
664 }
665}
666
667fn check_single_wordlength(num_bits: Option<u32>, required: u32) -> Result<TranslationPreference> {
668 if let Some(num_bits) = num_bits {
669 if num_bits == required {
670 Ok(TranslationPreference::Yes)
671 } else {
672 Ok(TranslationPreference::No)
673 }
674 } else {
675 Ok(TranslationPreference::No)
676 }
677}
678
679fn match_variable_type_name(variable_type_name: &Option<String>, candidates: &[String]) -> bool {
680 if let Some(type_name) = variable_type_name {
681 for candidate in candidates {
682 if type_name.eq_ignore_ascii_case(candidate) {
683 return true;
684 }
685 }
686 }
687 false
688}