1use std::cell::{Cell, RefCell};
22use std::collections::HashMap;
23use std::fmt;
24
25use anyhow::{Context, Result, bail};
26use serde::de::{self, Deserializer, Visitor};
27use serde::ser::Serializer;
28use serde::{Deserialize, Serialize};
29
30use crate::charset;
31use crate::decompile::{self, Element, Settings, UnitOp};
32use crate::names::NameTable;
33use crate::prolog::{MAX_DEPTH, ObjectType, SEMI, object_size, read_field};
34
35const MAX_HEX_NIBBLES: usize = 4096;
37const MAX_MEMORY_OBJECT: usize = 1 << 20;
39pub const MAX_DECODED_OBJECTS: usize = 1 << 18;
49pub const MAX_DECODED_NIBBLES: usize = 1 << 21;
53pub const MAX_CLONED_NIBBLES: usize = 1 << 24;
60const MAX_ARRAY_DIMS: usize = 2;
64const F64_EXPONENTS: std::ops::RangeInclusive<i32> = -307..=307;
66
67#[derive(Clone, Copy, Debug, PartialEq, Eq)]
70pub struct Real {
71 pub negative: bool,
73 pub digits: [u8; 12],
76 pub exponent: i32,
78}
79
80impl Real {
81 pub const ZERO: Real = Real {
83 negative: false,
84 digits: [0; 12],
85 exponent: 0,
86 };
87
88 pub fn from_body(body: &[u8]) -> Result<Real> {
91 let body = body
92 .get(..16)
93 .context("real body shorter than 16 nibbles")?;
94 if body.iter().any(|&d| d > 9) {
95 bail!("real body is not BCD: {}", hex(body));
96 }
97 let e = i32::from(body[0]) + 10 * i32::from(body[1]) + 100 * i32::from(body[2]);
98 let exponent = if e >= 500 { e - 1000 } else { e };
99 let mut digits = [0u8; 12];
100 for (i, d) in digits.iter_mut().enumerate() {
101 *d = body[14 - i];
102 }
103 let negative = match body[15] {
104 0 => false,
105 9 => true,
106 s => bail!("real sign nibble {s} is neither 0 nor 9"),
107 };
108 Ok(Real {
109 negative,
110 digits,
111 exponent,
112 })
113 }
114
115 pub fn to_body(&self) -> [u8; 16] {
117 let mut body = [0u8; 16];
118 let e = u32::try_from(self.exponent.rem_euclid(1000)).unwrap_or(0);
119 body[0] = (e % 10) as u8;
120 body[1] = (e / 10 % 10) as u8;
121 body[2] = (e / 100) as u8;
122 for (i, d) in self.digits.iter().enumerate() {
123 body[14 - i] = *d;
124 }
125 body[15] = if self.negative { 9 } else { 0 };
126 body
127 }
128
129 fn is_zero(&self) -> bool {
130 self.digits.iter().all(|&d| d == 0)
131 }
132
133 pub fn to_f64(&self) -> f64 {
135 format!("{}e{}", self.mantissa_text(), self.exponent)
136 .parse()
137 .unwrap_or(f64::NAN)
138 }
139
140 fn mantissa_text(&self) -> String {
142 let mut s = String::new();
143 if self.negative {
144 s.push('-');
145 }
146 s.push(char::from(b'0' + self.digits[0]));
147 s.push('.');
148 let last = self.digits.iter().rposition(|&d| d != 0).unwrap_or(0);
149 for d in &self.digits[1..=last] {
150 s.push(char::from(b'0' + d));
151 }
152 s
153 }
154
155 pub fn to_source(&self) -> String {
158 if self.exponent == 0 {
159 self.mantissa_text()
160 } else {
161 format!("{}E{}", self.mantissa_text(), self.exponent)
162 }
163 }
164
165 pub fn parse(text: &str) -> Result<Real> {
168 let bad = || anyhow::anyhow!("not a real number: {text:?}");
169 let t = text.trim();
170 let (negative, t) = match t.strip_prefix('-') {
171 Some(rest) => (true, rest),
172 None => (false, t.strip_prefix('+').unwrap_or(t)),
173 };
174 let (mant, exp) = match t.find(['e', 'E']) {
175 Some(i) => (&t[..i], t[i + 1..].parse::<i32>().map_err(|_| bad())?),
176 None => (t, 0),
177 };
178 let (int, frac) = mant.split_once('.').unwrap_or((mant, ""));
179 if (int.is_empty() && frac.is_empty())
180 || !int.bytes().chain(frac.bytes()).all(|b| b.is_ascii_digit())
181 {
182 return Err(bad());
183 }
184 let all: Vec<u8> = int.bytes().chain(frac.bytes()).map(|b| b - b'0').collect();
185 let Some(first) = all.iter().position(|&d| d != 0) else {
186 return Ok(Real::ZERO);
187 };
188 let int_len = i32::try_from(int.len()).map_err(|_| bad())?;
191 let first_i = i32::try_from(first).map_err(|_| bad())?;
192 let mut exponent = int_len - 1 - first_i + exp;
193 let sig = &all[first..];
194 let mut digits = [0u8; 12];
195 for (d, s) in digits.iter_mut().zip(sig) {
196 *d = *s;
197 }
198 if sig.get(12).is_some_and(|&d| d >= 5) {
199 let mut i = 12;
201 loop {
202 if i == 0 {
203 digits = [0; 12];
204 digits[0] = 1;
205 exponent += 1;
206 break;
207 }
208 i -= 1;
209 if digits[i] == 9 {
210 digits[i] = 0;
211 } else {
212 digits[i] += 1;
213 break;
214 }
215 }
216 }
217 if !(-499..=499).contains(&exponent) {
218 bail!("{text:?} is outside the calculator's range (exponents -499 to 499)");
219 }
220 Ok(Real {
221 negative,
222 digits,
223 exponent,
224 })
225 }
226
227 pub fn from_f64(v: f64) -> Result<Real> {
229 if !v.is_finite() {
230 bail!("{v} is not a finite number");
231 }
232 Real::parse(&format!("{v:e}"))
233 }
234}
235
236impl fmt::Display for Real {
237 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
238 f.write_str(&self.to_source())
239 }
240}
241
242impl Serialize for Real {
243 fn serialize<S: Serializer>(&self, s: S) -> std::result::Result<S::Ok, S::Error> {
244 if self.is_zero() || F64_EXPONENTS.contains(&self.exponent) {
245 s.serialize_f64(self.to_f64())
246 } else {
247 s.serialize_str(&self.to_source())
248 }
249 }
250}
251
252impl<'de> Deserialize<'de> for Real {
253 fn deserialize<D: Deserializer<'de>>(d: D) -> std::result::Result<Real, D::Error> {
254 struct V;
255 impl Visitor<'_> for V {
256 type Value = Real;
257 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
258 f.write_str("a number or decimal text such as \"1.5E-400\"")
259 }
260 fn visit_f64<E: de::Error>(self, v: f64) -> std::result::Result<Real, E> {
261 Real::from_f64(v).map_err(E::custom)
262 }
263 fn visit_i64<E: de::Error>(self, v: i64) -> std::result::Result<Real, E> {
264 Real::parse(&v.to_string()).map_err(E::custom)
265 }
266 fn visit_u64<E: de::Error>(self, v: u64) -> std::result::Result<Real, E> {
267 Real::parse(&v.to_string()).map_err(E::custom)
268 }
269 fn visit_str<E: de::Error>(self, v: &str) -> std::result::Result<Real, E> {
270 Real::parse(v).map_err(E::custom)
271 }
272 }
273 d.deserialize_any(V)
274 }
275}
276
277#[derive(Clone, Debug, PartialEq, Eq)]
279pub struct Integer {
280 pub negative: bool,
282 pub digits: String,
285}
286
287impl Integer {
288 pub fn parse(text: &str) -> Result<Integer> {
290 let t = text.trim();
291 let (negative, d) = match t.strip_prefix('-') {
292 Some(rest) => (true, rest),
293 None => (false, t.strip_prefix('+').unwrap_or(t)),
294 };
295 if d.is_empty() || !d.bytes().all(|b| b.is_ascii_digit()) {
296 bail!("not an integer: {text:?}");
297 }
298 let digits = d.trim_start_matches('0');
299 let digits = if digits.is_empty() { "0" } else { digits };
300 Ok(Integer {
301 negative: negative && digits != "0",
302 digits: digits.to_string(),
303 })
304 }
305
306 pub fn from_nibbles(n: &[u8]) -> Result<Integer> {
309 let Some((&sign, digits)) = n.split_last() else {
310 bail!("empty integer");
311 };
312 if n.iter().any(|&d| d > 9) {
313 bail!("integer body is not BCD: {}", hex(n));
314 }
315 let text: String = digits.iter().rev().map(|d| char::from(b'0' + d)).collect();
316 let mut i = Integer::parse(if text.is_empty() { "0" } else { &text })?;
317 i.negative = sign == 9 && i.digits != "0";
318 Ok(i)
319 }
320
321 pub fn to_nibbles(&self) -> Vec<u8> {
323 if self.digits == "0" {
324 return vec![0];
325 }
326 let mut n: Vec<u8> = self.digits.bytes().rev().map(|b| b - b'0').collect();
327 n.push(if self.negative { 9 } else { 0 });
328 n
329 }
330
331 pub fn to_source(&self) -> String {
333 if self.negative {
334 format!("-{}", self.digits)
335 } else {
336 self.digits.clone()
337 }
338 }
339}
340
341impl Serialize for Integer {
342 fn serialize<S: Serializer>(&self, s: S) -> std::result::Result<S::Ok, S::Error> {
343 match self.to_source().parse::<i64>() {
345 Ok(v) if self.digits.len() <= 15 => s.serialize_i64(v),
346 _ => s.serialize_str(&self.to_source()),
347 }
348 }
349}
350
351impl<'de> Deserialize<'de> for Integer {
352 fn deserialize<D: Deserializer<'de>>(d: D) -> std::result::Result<Integer, D::Error> {
353 struct V;
354 impl Visitor<'_> for V {
355 type Value = Integer;
356 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
357 f.write_str("an integer or a string of decimal digits")
358 }
359 fn visit_i64<E: de::Error>(self, v: i64) -> std::result::Result<Integer, E> {
360 Integer::parse(&v.to_string()).map_err(E::custom)
361 }
362 fn visit_u64<E: de::Error>(self, v: u64) -> std::result::Result<Integer, E> {
363 Integer::parse(&v.to_string()).map_err(E::custom)
364 }
365 fn visit_f64<E: de::Error>(self, v: f64) -> std::result::Result<Integer, E> {
366 if v.fract() != 0.0 || !v.is_finite() || v.abs() > 9.0e15 {
367 return Err(E::custom(format!("{v} is not an exact integer")));
368 }
369 Integer::parse(&format!("{v:.0}")).map_err(E::custom)
370 }
371 fn visit_str<E: de::Error>(self, v: &str) -> std::result::Result<Integer, E> {
372 Integer::parse(v).map_err(E::custom)
373 }
374 }
375 d.deserialize_any(V)
376 }
377}
378
379#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
381#[serde(rename_all = "lowercase")]
382pub enum Base {
383 Hex,
385 Dec,
387 Oct,
389 Bin,
391}
392
393impl Base {
394 pub fn from_display(text: &str) -> Option<Base> {
396 let t = text.trim();
397 if !t.starts_with('#') {
398 return None;
399 }
400 match t.chars().last()? {
401 'h' => Some(Base::Hex),
402 'd' => Some(Base::Dec),
403 'o' => Some(Base::Oct),
404 'b' => Some(Base::Bin),
405 _ => None,
406 }
407 }
408
409 pub fn format(self, value: u64) -> String {
411 match self {
412 Base::Hex => format!("# {value:X}h"),
413 Base::Dec => format!("# {value}d"),
414 Base::Oct => format!("# {value:o}o"),
415 Base::Bin => format!("# {value:b}b"),
416 }
417 }
418}
419
420#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
422#[serde(untagged)]
423pub enum ArrayItem {
424 Row(Vec<ArrayItem>),
426 Item(Box<Object>),
428}
429
430#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
432#[serde(tag = "type", rename_all = "snake_case")]
433pub enum Object {
434 Real {
436 value: Real,
438 },
439 Integer {
441 value: Integer,
443 },
444 Complex {
446 re: Real,
448 im: Real,
450 },
451 String {
453 value: String,
455 },
456 Name {
458 value: String,
460 },
461 LocalName {
463 value: String,
465 },
466 Character {
468 value: String,
470 },
471 Binary {
473 value: u64,
475 #[serde(default, skip_serializing_if = "Option::is_none")]
477 base: Option<Base>,
478 #[serde(default, skip_serializing_if = "Option::is_none")]
480 text: Option<String>,
481 },
482 List {
484 items: Vec<Object>,
486 },
487 Tagged {
489 tag: String,
491 object: Box<Object>,
493 },
494 Unit {
496 value: Real,
498 #[serde(default, skip_serializing_if = "Option::is_none")]
500 unit: Option<String>,
501 },
502 Array {
504 dims: Vec<usize>,
506 items: Vec<ArrayItem>,
508 },
509 Program {
511 #[serde(default, skip_serializing_if = "Option::is_none")]
513 source: Option<String>,
514 },
515 Algebraic {
517 #[serde(default, skip_serializing_if = "Option::is_none")]
519 source: Option<String>,
520 },
521 Command {
524 #[serde(default, skip_serializing_if = "Option::is_none")]
527 name: Option<String>,
528 #[serde(default, skip_serializing_if = "Option::is_none")]
530 address: Option<u32>,
531 #[serde(default, skip_serializing_if = "Option::is_none")]
535 library: Option<u16>,
536 #[serde(default, skip_serializing_if = "Option::is_none")]
538 command: Option<u16>,
539 #[serde(default, skip_serializing_if = "Option::is_none")]
541 source: Option<String>,
542 },
543 Unknown {
545 prolog: String,
547 #[serde(default, skip_serializing_if = "Option::is_none")]
549 kind: Option<String>,
550 nibbles: usize,
552 hex: String,
554 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
556 truncated: bool,
557 #[serde(default, skip_serializing_if = "Option::is_none")]
559 source: Option<String>,
560 },
561}
562
563pub trait Memory {
565 fn nibble(&self, addr: u32) -> Option<u8>;
567}
568
569#[derive(Clone, Copy, Debug, Default)]
571pub struct NoMemory;
572
573impl Memory for NoMemory {
574 fn nibble(&self, _addr: u32) -> Option<u8> {
575 None
576 }
577}
578
579fn hex(nibbles: &[u8]) -> String {
580 nibbles
581 .iter()
582 .map(|&n| char::from_digit(u32::from(n & 0xF), 16).unwrap_or('?'))
583 .collect::<String>()
584 .to_uppercase()
585}
586
587fn field(n: &[u8], at: usize, width: usize) -> Result<u32> {
588 read_field(n, at, width).with_context(|| format!("object truncated at nibble {at}"))
589}
590
591fn usize_field(n: &[u8], at: usize, width: usize) -> Result<usize> {
592 Ok(usize::try_from(field(n, at, width)?)?)
593}
594
595fn chars(n: &[u8], at: usize, count: usize) -> Result<String> {
597 let bytes = (0..count)
598 .map(|i| field(n, at + 2 * i, 2).map(|b| b as u8))
599 .collect::<Result<Vec<u8>>>()?;
600 Ok(charset::decode(&bytes))
601}
602
603pub fn decode(nibbles: &[u8], mem: &dyn Memory) -> Result<Object> {
606 Reader::new(mem).decode(nibbles)
607}
608
609pub fn decode_at(addr: u32, mem: &dyn Memory) -> Result<Object> {
613 Reader::new(mem).decode_at(addr)
614}
615
616pub struct Reader<'a> {
621 decoder: Decoder<'a>,
622}
623
624impl fmt::Debug for Reader<'_> {
625 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
626 f.debug_struct("Reader")
627 .field("budget", &self.decoder.budget.get())
628 .field("nibbles", &self.decoder.nibbles.get())
629 .field("names", &self.decoder.names.is_some())
630 .finish_non_exhaustive()
631 }
632}
633
634impl<'a> Reader<'a> {
635 pub fn new(mem: &'a dyn Memory) -> Self {
638 Self {
639 decoder: Decoder::new(mem, None, Settings::default()),
640 }
641 }
642
643 pub fn with_names(mem: &'a dyn Memory, names: &'a NameTable, settings: Settings) -> Self {
646 Self {
647 decoder: Decoder::new(mem, Some(names), settings),
648 }
649 }
650
651 pub fn decode_at(&self, addr: u32) -> Result<Object> {
655 let d = &self.decoder;
656 if let Some(info) = d.names.and_then(|t| t.command_at(addr, d.mem)) {
657 d.charge(1)?;
658 return Ok(Object::Command {
659 name: info.name.map(str::to_string),
660 address: Some(addr),
661 library: Some(info.library),
662 command: Some(info.number),
663 source: None,
664 });
665 }
666 d.rom_object(addr, 0)
667 }
668
669 pub fn decode(&self, nibbles: &[u8]) -> Result<Object> {
671 match self.decoder.object(nibbles, 0, 0) {
672 Ok((obj, _)) => Ok(obj),
673 Err(e) if self.decoder.exhausted.get() => Err(e),
675 Err(_) => {
676 let prolog = field(nibbles, 0, 5)?;
677 Ok(unknown(prolog, nibbles))
678 }
679 }
680 }
681}
682
683fn unknown(prolog: u32, nibbles: &[u8]) -> Object {
684 let shown = nibbles.len().min(MAX_HEX_NIBBLES);
685 Object::Unknown {
686 prolog: format!("{prolog:05X}"),
687 kind: ObjectType::from_prolog(prolog).map(|t| t.name().to_string()),
688 nibbles: nibbles.len(),
689 hex: hex(&nibbles[..shown]),
690 truncated: shown < nibbles.len(),
691 source: None,
692 }
693}
694
695struct Decoder<'a> {
696 mem: &'a dyn Memory,
697 names: Option<&'a NameTable>,
700 settings: Settings,
702 budget: Cell<usize>,
704 nibbles: Cell<usize>,
706 cloned: Cell<usize>,
708 seen: RefCell<HashMap<(u32, usize), (Object, Cost)>>,
713 exhausted: Cell<bool>,
715}
716
717impl<'a> Decoder<'a> {
718 fn new(mem: &'a dyn Memory, names: Option<&'a NameTable>, settings: Settings) -> Self {
719 Self {
720 mem,
721 names,
722 settings,
723 budget: Cell::new(MAX_DECODED_OBJECTS),
724 nibbles: Cell::new(MAX_DECODED_NIBBLES),
725 cloned: Cell::new(MAX_CLONED_NIBBLES),
726 seen: RefCell::new(HashMap::new()),
727 exhausted: Cell::new(false),
728 }
729 }
730
731 fn charge(&self, count: usize) -> Result<()> {
733 let Some(left) = self.budget.get().checked_sub(count) else {
734 self.exhausted.set(true);
735 bail!(
736 "more than {MAX_DECODED_OBJECTS} objects (MAX_DECODED_OBJECTS) to decode: \
737 pointers that repeat, memory corrupt or not set up"
738 );
739 };
740 self.budget.set(left);
741 Ok(())
742 }
743
744 fn charge_nibbles(&self, count: usize) -> Result<()> {
746 let Some(left) = self.nibbles.get().checked_sub(count) else {
747 self.exhausted.set(true);
748 bail!(
749 "more than {MAX_DECODED_NIBBLES} nibbles (MAX_DECODED_NIBBLES) to decode: \
750 objects that repeat, memory corrupt or not set up"
751 );
752 };
753 self.nibbles.set(left);
754 Ok(())
755 }
756
757 fn charge_cloned(&self, count: usize) -> Result<()> {
759 let Some(left) = self.cloned.get().checked_sub(count) else {
760 self.exhausted.set(true);
761 bail!(
762 "more than {MAX_CLONED_NIBBLES} nibbles (MAX_CLONED_NIBBLES) of repeated \
763 objects to decode: pointers that repeat, memory corrupt or not set up"
764 );
765 };
766 self.cloned.set(left);
767 Ok(())
768 }
769
770 fn left(&self) -> Cost {
773 Cost {
774 objects: self.budget.get(),
775 nibbles: self.nibbles.get() + self.cloned.get(),
776 }
777 }
778
779 fn object(&self, n: &[u8], at: usize, depth: usize) -> Result<(Object, usize)> {
781 if depth > MAX_DEPTH {
782 bail!("objects nested deeper than {MAX_DEPTH} levels");
783 }
784 self.charge(1)?;
785 let prolog = field(n, at, 5)?;
786 let ty = ObjectType::from_prolog(prolog)
787 .with_context(|| format!("unknown prolog {prolog:05X} at nibble {at}"))?;
788 let size = object_size(n, at)?;
789 let body = at + 5;
790 let obj = match ty {
791 ObjectType::Real => Object::Real {
792 value: Real::from_body(&n[body..])?,
793 },
794 ObjectType::Complex => Object::Complex {
795 re: Real::from_body(&n[body..])?,
796 im: Real::from_body(&n[body + 16..])?,
797 },
798 ObjectType::String => {
799 let len = usize_field(n, body, 5)?;
800 let count = len.checked_sub(5).context("string length too small")?;
801 if count % 2 != 0 {
802 bail!("string of an odd number of nibbles");
803 }
804 Object::String {
805 value: chars(n, body + 5, count / 2)?,
806 }
807 }
808 ObjectType::BinaryInteger => {
809 let len = usize_field(n, body, 5)?;
810 let count = len.checked_sub(5).context("binary length too small")?;
811 if count > 16 {
812 return Ok((unknown(prolog, &n[at..at + size]), size));
813 }
814 let value = n[body + 5..body + 5 + count]
815 .iter()
816 .rev()
817 .fold(0u64, |acc, &d| (acc << 4) | u64::from(d));
818 Object::Binary {
819 value,
820 base: None,
821 text: None,
822 }
823 }
824 ObjectType::Integer => {
825 let len = usize_field(n, body, 5)?;
826 let count = len.checked_sub(5).context("integer length too small")?;
827 Object::Integer {
828 value: Integer::from_nibbles(&n[body + 5..body + 5 + count])?,
829 }
830 }
831 ObjectType::GlobalName | ObjectType::LocalName => {
832 let count = usize_field(n, body, 2)?;
833 let value = chars(n, body + 2, count)?;
834 if ty == ObjectType::GlobalName {
835 Object::Name { value }
836 } else {
837 Object::LocalName { value }
838 }
839 }
840 ObjectType::Character => Object::Character {
841 value: chars(n, body, 1)?,
842 },
843 ObjectType::List => Object::List {
844 items: self
845 .elements(n, body, depth)?
846 .into_iter()
847 .map(element_object)
848 .collect(),
849 },
850 ObjectType::Tagged => {
851 let count = usize_field(n, body, 2)?;
852 let tag = chars(n, body + 2, count)?;
853 let (object, _) = self.element(n, body + 2 + 2 * count, depth)?;
854 Object::Tagged {
855 tag,
856 object: Box::new(element_object(object)),
857 }
858 }
859 ObjectType::Unit => {
860 let mut elements = self.elements(n, body, depth)?.into_iter();
861 let Some(Element::Object(Object::Real { value })) = elements.next() else {
862 bail!("unit does not start with a real");
863 };
864 let rest: Vec<Element> = elements.collect();
865 let unit = self
866 .names
867 .and_then(|_| decompile::unit(&rest, &self.settings));
868 Object::Unit { value, unit }
869 }
870 ObjectType::Array => self.array(n, at, size)?,
871 ObjectType::SymbolicMatrix => self.symbolic_matrix(n, body, depth)?,
872 ObjectType::Program => {
873 let elements = self.elements(n, body, depth)?;
874 Object::Program {
875 source: self
876 .names
877 .map(|_| decompile::program(&elements, &self.settings)),
878 }
879 }
880 ObjectType::Algebraic => self.symbolic(n, body, depth)?.0,
881 ObjectType::XlibName => element_object(self.xlib(n, body)?),
882 _ => unknown(prolog, &n[at..at + size]),
883 };
884 Ok((obj, size))
885 }
886
887 fn symbolic(&self, n: &[u8], body: usize, depth: usize) -> Result<(Object, Vec<Element>)> {
889 let elements = self.elements(n, body, depth)?;
890 let object = Object::Algebraic {
891 source: self
892 .names
893 .and_then(|_| decompile::algebraic(&elements, &self.settings)),
894 };
895 Ok((object, elements))
896 }
897
898 fn elements(&self, n: &[u8], mut at: usize, depth: usize) -> Result<Vec<Element>> {
900 let mut items = Vec::new();
901 while field(n, at, 5)? != SEMI {
902 let (obj, size) = self.element(n, at, depth)?;
903 items.push(obj);
904 at += size;
905 }
906 Ok(items)
907 }
908
909 fn element(&self, n: &[u8], at: usize, depth: usize) -> Result<(Element, usize)> {
911 let p = field(n, at, 5)?;
912 match ObjectType::from_prolog(p) {
913 Some(ObjectType::XlibName) => {
914 self.charge(1)?;
915 Ok((self.xlib(n, at + 5)?, 11))
916 }
917 Some(ObjectType::SystemBinary) => {
918 self.charge(1)?;
919 Ok((Element::SystemBinary(field(n, at + 5, 5)?), 10))
920 }
921 Some(ObjectType::Algebraic) if depth < MAX_DEPTH => {
922 self.charge(1)?;
923 let size = object_size(n, at)?;
924 let (object, elements) = self.symbolic(n, at + 5, depth + 1)?;
925 Ok((Element::Symbolic { object, elements }, size))
926 }
927 Some(_) => {
928 let (obj, size) = self.object(n, at, depth + 1)?;
929 Ok((Element::Object(obj), size))
930 }
931 None => Ok((self.pointer(p, depth + 1)?, 5)),
932 }
933 }
934
935 fn xlib(&self, n: &[u8], at: usize) -> Result<Element> {
937 let lib = field(n, at, 3)?;
938 let cmd = field(n, at + 3, 3)?;
939 let (lib, cmd) = (lib as u16, cmd as u16);
940 let info = self.names.and_then(|t| t.xlib(lib, cmd));
941 Ok(Element::Command {
942 name: info.and_then(|i| i.name).map(str::to_string),
943 address: None,
944 xlib: Some((lib, cmd)),
945 arity: info.and_then(|i| i.arity),
946 silent: false,
947 })
948 }
949
950 fn pointer(&self, p: u32, depth: usize) -> Result<Element> {
954 if let Some(t) = self.names {
955 if let Some(info) = t.command_at(p, self.mem) {
956 self.charge(1)?;
957 return Ok(Element::Command {
958 name: info.name.map(str::to_string),
959 address: Some(p),
960 xlib: Some((info.library, info.number)),
961 arity: info.arity,
962 silent: info.silent,
963 });
964 }
965 if let Some(m) = t.unit_markers() {
966 let op = [
967 (m.times, UnitOp::Times),
968 (m.divide, UnitOp::Divide),
969 (m.power, UnitOp::Power),
970 (m.prefix, UnitOp::Prefix),
971 (m.end, UnitOp::End),
972 ]
973 .into_iter()
974 .find_map(|(a, op)| (a == p).then_some(op));
975 if let Some(op) = op {
976 self.charge(1)?;
977 return Ok(Element::Unit(op));
978 }
979 }
980 }
981 let read = |at: u32, width: u32| -> Option<u32> {
982 (0..width).rev().try_fold(0u32, |v, i| {
983 Some((v << 4) | u32::from(self.mem.nibble(at.wrapping_add(i))?))
984 })
985 };
986 let command = Element::Command {
987 name: None,
988 address: Some(p),
989 xlib: None,
990 arity: None,
991 silent: false,
992 };
993 match read(p, 5).and_then(ObjectType::from_prolog) {
994 None | Some(ObjectType::Program | ObjectType::Code) => {
997 self.charge(1)?;
998 Ok(command)
999 }
1000 Some(ObjectType::SystemBinary) => {
1001 self.charge(1)?;
1002 Ok(read(p + 5, 5).map_or(command, Element::SystemBinary))
1003 }
1004 Some(ObjectType::XlibName) => {
1005 self.charge(1)?;
1006 let body: Option<Vec<u8>> = (0..6).map(|i| self.mem.nibble(p + 5 + i)).collect();
1007 match body {
1008 Some(b) => self.xlib(&b, 0),
1009 None => Ok(command),
1010 }
1011 }
1012 Some(_) => Ok(Element::Object(self.rom_object(p, depth)?)),
1013 }
1014 }
1015
1016 fn rom_object(&self, addr: u32, depth: usize) -> Result<Object> {
1019 if let Some((obj, cost)) = self.seen.borrow().get(&(addr, depth)) {
1020 self.charge(cost.objects)?;
1021 self.charge_cloned(cost.nibbles)?;
1022 return Ok(obj.clone());
1023 }
1024 let before = self.left();
1025 let obj = self.read_object(addr, depth)?;
1026 let after = self.left();
1027 let cost = Cost {
1028 objects: before.objects - after.objects,
1029 nibbles: before.nibbles - after.nibbles,
1030 };
1031 self.seen
1032 .borrow_mut()
1033 .insert((addr, depth), (obj.clone(), cost));
1034 Ok(obj)
1035 }
1036
1037 fn read_object(&self, addr: u32, depth: usize) -> Result<Object> {
1039 let read = |len: usize| -> Vec<u8> {
1041 (0..len)
1042 .map_while(|i| {
1043 u32::try_from(i)
1044 .ok()
1045 .and_then(|i| self.mem.nibble(addr.wrapping_add(i)))
1046 })
1047 .collect()
1048 };
1049 let head = read(5);
1050 if read_field(&head, 0, 5)
1051 .and_then(ObjectType::from_prolog)
1052 .is_none()
1053 {
1054 self.charge(1)?;
1057 return Ok(Object::Command {
1058 name: None,
1059 address: Some(addr),
1060 library: None,
1061 command: None,
1062 source: None,
1063 });
1064 }
1065 let mut len = 64;
1066 loop {
1067 let nib = read(len);
1068 match object_size(&nib, 0) {
1069 Ok(size) => {
1070 self.charge_nibbles(size)?;
1071 return Ok(self.object(&nib, 0, depth)?.0);
1072 }
1073 Err(_) if nib.len() == len && len < MAX_MEMORY_OBJECT => len *= 8,
1074 Err(e) => bail!("object at #{addr:05X}: {e}"),
1075 }
1076 }
1077 }
1078
1079 fn symbolic_matrix(&self, n: &[u8], body: usize, depth: usize) -> Result<Object> {
1082 let items: Vec<Object> = self
1083 .elements(n, body, depth)?
1084 .into_iter()
1085 .map(element_object)
1086 .collect();
1087 let rows = !items.is_empty()
1088 && items
1089 .iter()
1090 .all(|o| matches!(o, Object::Array { dims, .. } if dims.len() == 1));
1091 if !rows {
1092 return Ok(Object::Array {
1093 dims: vec![items.len()],
1094 items: items
1095 .into_iter()
1096 .map(|o| ArrayItem::Item(Box::new(o)))
1097 .collect(),
1098 });
1099 }
1100 let cols = match items.first() {
1101 Some(Object::Array { dims, .. }) => dims[0],
1102 _ => 0,
1103 };
1104 Ok(Object::Array {
1105 dims: vec![items.len(), cols],
1106 items: items
1107 .into_iter()
1108 .map(|o| match o {
1109 Object::Array { items, .. } => ArrayItem::Row(items),
1110 o => ArrayItem::Item(Box::new(o)),
1111 })
1112 .collect(),
1113 })
1114 }
1115
1116 fn array(&self, n: &[u8], at: usize, size: usize) -> Result<Object> {
1118 let body = at + 5;
1119 let elem = field(n, body + 5, 5)?;
1120 let ndims = usize_field(n, body + 10, 5)?;
1121 if ndims > MAX_ARRAY_DIMS {
1122 bail!("array of {ndims} dimensions (at most {MAX_ARRAY_DIMS})");
1123 }
1124 let bad = || anyhow::anyhow!("array dimensions do not match its length");
1125 let start = ndims
1128 .checked_mul(5)
1129 .and_then(|d| d.checked_add(body + 15))
1130 .filter(|&s| s <= at + size)
1131 .ok_or_else(bad)?;
1132 let dims = (0..ndims)
1133 .map(|i| usize_field(n, body + 15 + 5 * i, 5))
1134 .collect::<Result<Vec<usize>>>()?;
1135 let width = match ObjectType::from_prolog(elem) {
1136 Some(ObjectType::Real) => 16,
1137 Some(ObjectType::Complex) => 32,
1138 _ => return Ok(unknown(field(n, at, 5)?, &n[at..at + size])),
1139 };
1140 let mut count: usize = 1;
1143 for d in &dims {
1144 count = count
1145 .checked_mul(*d)
1146 .filter(|&c| c <= size)
1147 .ok_or_else(bad)?;
1148 }
1149 let end = count
1150 .checked_mul(width)
1151 .and_then(|b| b.checked_add(start))
1152 .ok_or_else(bad)?;
1153 if ndims == 0 || end > at + size {
1154 return Err(bad());
1155 }
1156 self.charge(count)?;
1157 let mut flat = Vec::with_capacity(count);
1158 for i in 0..count {
1159 let p = start + i * width;
1160 flat.push(if width == 16 {
1161 Object::Real {
1162 value: Real::from_body(&n[p..])?,
1163 }
1164 } else {
1165 Object::Complex {
1166 re: Real::from_body(&n[p..])?,
1167 im: Real::from_body(&n[p + 16..])?,
1168 }
1169 });
1170 }
1171 Ok(Object::Array {
1172 items: nest(&dims, &mut flat.into_iter()),
1173 dims,
1174 })
1175 }
1176}
1177
1178#[derive(Clone, Copy, Debug)]
1180struct Cost {
1181 objects: usize,
1182 nibbles: usize,
1183}
1184
1185fn element_object(e: Element) -> Object {
1187 match e {
1188 Element::Object(o) | Element::Symbolic { object: o, .. } => o,
1189 Element::Command {
1190 name,
1191 address,
1192 xlib,
1193 ..
1194 } => Object::Command {
1195 name,
1196 address,
1197 library: xlib.map(|x| x.0),
1198 command: xlib.map(|x| x.1),
1199 source: None,
1200 },
1201 Element::SystemBinary(v) => Object::Unknown {
1202 prolog: format!("{:05X}", ObjectType::SystemBinary.prolog()),
1203 kind: Some(ObjectType::SystemBinary.name().to_string()),
1204 nibbles: 10,
1205 hex: format!("{:05X}", ObjectType::SystemBinary.prolog())
1206 .chars()
1207 .rev()
1208 .chain(format!("{v:05X}").chars().rev())
1209 .collect(),
1210 truncated: false,
1211 source: None,
1212 },
1213 Element::Unit(_) => Object::List { items: Vec::new() },
1215 }
1216}
1217
1218fn nest(dims: &[usize], flat: &mut impl Iterator<Item = Object>) -> Vec<ArrayItem> {
1221 match dims {
1222 [] => Vec::new(),
1223 [n] => flat
1224 .take(*n)
1225 .map(|o| ArrayItem::Item(Box::new(o)))
1226 .collect(),
1227 [n, rest @ ..] => (0..*n).map(|_| ArrayItem::Row(nest(rest, flat))).collect(),
1228 }
1229}
1230
1231impl Object {
1232 pub fn needs_source(&self) -> bool {
1235 match self {
1236 Object::Program { source } | Object::Algebraic { source } => source.is_none(),
1237 Object::Command { name, source, .. } => name.is_none() && source.is_none(),
1238 Object::Unit { unit, .. } => unit.is_none(),
1239 Object::Unknown { source, .. } => source.is_none(),
1240 Object::List { items } => items.iter().any(Object::needs_source),
1241 Object::Tagged { object, .. } => object.needs_source(),
1242 _ => false,
1243 }
1244 }
1245
1246 pub fn set_base(&mut self, b: Base) {
1248 match self {
1249 Object::Binary { value, base, text } => {
1250 *base = Some(b);
1251 *text = Some(b.format(*value));
1252 }
1253 Object::List { items } => items.iter_mut().for_each(|o| o.set_base(b)),
1254 Object::Tagged { object, .. } => object.set_base(b),
1255 _ => {}
1256 }
1257 }
1258
1259 pub fn has_binary(&self) -> bool {
1261 match self {
1262 Object::Binary { .. } => true,
1263 Object::List { items } => items.iter().any(Object::has_binary),
1264 Object::Tagged { object, .. } => object.has_binary(),
1265 _ => false,
1266 }
1267 }
1268}
1269#[cfg(test)]
1270mod tests {
1271 use super::*;
1272 use serde_json::json;
1273
1274 fn nib(s: &str) -> Vec<u8> {
1276 s.chars().map(|c| c.to_digit(16).unwrap() as u8).collect()
1277 }
1278
1279 fn dec(s: &str) -> Object {
1280 decode(&nib(s), &NoMemory).unwrap()
1281 }
1282
1283 fn real(s: &str) -> Real {
1284 Real::parse(s).unwrap()
1285 }
1286
1287 fn cmd(address: u32) -> Object {
1289 Object::Command {
1290 name: None,
1291 address: Some(address),
1292 library: None,
1293 command: None,
1294 source: None,
1295 }
1296 }
1297
1298 fn put_field(out: &mut Vec<u8>, value: u64, width: usize) {
1299 out.extend((0..width).map(|i| ((value >> (4 * i)) & 0xF) as u8));
1300 }
1301
1302 #[test]
1306 fn reals_decode_exactly() {
1307 let o = dec("339207997389453562780");
1309 assert_eq!(
1310 o,
1311 Object::Real {
1312 value: real("8.72653549837E-3")
1313 }
1314 );
1315 assert_eq!(
1316 serde_json::to_value(&o).unwrap(),
1317 json!({"type": "real", "value": 0.00872653549837})
1318 );
1319 let o = dec("339200070000000000519");
1321 let Object::Real { value } = o else { panic!() };
1322 assert_eq!(value.to_source(), "-1.5E-300");
1323 assert!(value.negative);
1324 assert_eq!(
1325 dec("339200000000000000000"),
1326 Object::Real { value: Real::ZERO }
1327 );
1328 }
1329
1330 #[test]
1331 fn real_text_round_trips_and_rounds() {
1332 for s in [
1333 "4.79425538604E-1",
1334 "5.",
1335 "-1.5E-300",
1336 "9.99999999999E499",
1337 "1.E-499",
1338 "0.",
1339 ] {
1340 let r = real(s);
1341 assert_eq!(r.to_source(), s);
1342 assert_eq!(Real::from_body(&r.to_body()).unwrap(), r);
1343 }
1344 assert_eq!(real("0.479425538604").to_source(), "4.79425538604E-1");
1345 assert_eq!(real(".5").to_source(), "5.E-1");
1346 assert_eq!(real("123").to_source(), "1.23E2");
1347 assert_eq!(real("1.234567890125").to_source(), "1.23456789013");
1349 assert_eq!(real("9.999999999995").to_source(), "1.E1");
1350 assert_eq!(real("-0.000").to_source(), "0.");
1351 assert!(Real::parse("1E500").is_err());
1352 assert!(Real::parse("1.2.3").is_err());
1353 assert!(Real::parse("").is_err());
1354 assert!(Real::parse("e5").is_err());
1355 assert_eq!(
1356 Real::from_f64(0.479425538604).unwrap(),
1357 real("4.79425538604E-1")
1358 );
1359 }
1360
1361 #[test]
1362 fn reals_beyond_f64_serialize_as_text() {
1363 let r = Object::Real {
1364 value: real("1.5E-400"),
1365 };
1366 assert_eq!(
1367 serde_json::to_value(&r).unwrap(),
1368 json!({"type": "real", "value": "1.5E-400"})
1369 );
1370 let back: Object =
1371 serde_json::from_value(json!({"type": "real", "value": "1.5E-400"})).unwrap();
1372 assert_eq!(back, r);
1373 let five: Object = serde_json::from_value(json!({"type": "real", "value": 5})).unwrap();
1374 assert_eq!(five, Object::Real { value: real("5") });
1375 }
1376
1377 #[test]
1378 fn complex_string_binary() {
1379 assert_eq!(
1380 dec("7792000000000000000100000000000000029"),
1381 Object::Complex {
1382 re: real("1"),
1383 im: real("-2")
1384 }
1385 );
1386 assert_eq!(
1388 dec("C2A2011000849602BA87BB"),
1389 Object::String {
1390 value: "Hi «x»".into()
1391 }
1392 );
1393 let mut b = dec("E4A2051000FF00000000000000");
1394 assert_eq!(
1395 b,
1396 Object::Binary {
1397 value: 255,
1398 base: None,
1399 text: None
1400 }
1401 );
1402 b.set_base(Base::Hex);
1403 assert_eq!(
1404 serde_json::to_value(&b).unwrap(),
1405 json!({"type": "binary", "value": 255, "base": "hex", "text": "# FFh"})
1406 );
1407 assert_eq!(Base::from_display("# 255d"), Some(Base::Dec));
1408 assert_eq!(Base::from_display("42."), None);
1409 }
1410
1411 #[test]
1412 fn integers_49g() {
1413 assert_eq!(
1414 dec("416207000050"),
1415 Object::Integer {
1416 value: Integer::parse("5").unwrap()
1417 }
1418 );
1419 let big = dec("416209100098765432109876543219");
1420 assert_eq!(
1421 big,
1422 Object::Integer {
1423 value: Integer::parse("-1234567890123456789").unwrap()
1424 }
1425 );
1426 assert_eq!(
1427 serde_json::to_value(&big).unwrap(),
1428 json!({"type": "integer", "value": "-1234567890123456789"})
1429 );
1430 assert_eq!(
1431 dec("416206000000"),
1432 Object::Integer {
1433 value: Integer::parse("0").unwrap()
1434 }
1435 );
1436 assert_eq!(
1437 serde_json::to_value(dec("416207000050")).unwrap(),
1438 json!({"type": "integer", "value": 5})
1439 );
1440 }
1441
1442 #[test]
1443 fn names_tagged_and_lists_with_rom_pointers() {
1444 let list = dec(
1446 "47A209C2A2ED2A2C2A20700003784E20108547A20D13A2B2130E4A2051000A200000000000000B2130",
1447 );
1448 let Object::List { items } = &list else {
1449 panic!("{list:?}")
1450 };
1451 assert_eq!(items.len(), 6);
1452 assert_eq!(items[0], cmd(0x2A2C9));
1453 assert_eq!(items[2], Object::String { value: "s".into() });
1454 assert_eq!(items[3], Object::Name { value: "X".into() });
1455 assert_eq!(
1456 items[4],
1457 Object::List {
1458 items: vec![cmd(0x2A31D)]
1459 }
1460 );
1461 assert_eq!(
1462 items[5],
1463 Object::Binary {
1464 value: 42,
1465 base: None,
1466 text: None
1467 }
1468 );
1469 let t = dec("47A20CFA201045D13A2B2130");
1471 assert_eq!(
1472 t,
1473 Object::List {
1474 items: vec![Object::Tagged {
1475 tag: "T".into(),
1476 object: Box::new(cmd(0x2A31D))
1477 }]
1478 }
1479 );
1480 }
1481
1482 struct Rom;
1484 impl Memory for Rom {
1485 fn nibble(&self, addr: u32) -> Option<u8> {
1486 let (base, obj) = match addr {
1487 0x2A2C9..=0x2A2DD => (0x2A2C9, "339200000000000000010"),
1488 0x2A31D..=0x2A331 => (0x2A31D, "339200000000000000050"),
1489 0x10000..=0x10004 => (0x10000, "50001"),
1491 _ => return None,
1492 };
1493 nib(obj).get((addr - base) as usize).copied()
1494 }
1495 }
1496
1497 #[test]
1498 fn rom_pointers_resolve_through_memory() {
1499 let list = decode(&nib("47A209C2A2D13A200001B2130"), &Rom).unwrap();
1500 assert_eq!(
1501 list,
1502 Object::List {
1503 items: vec![
1504 Object::Real { value: real("1") },
1505 Object::Real { value: real("5") },
1506 cmd(0x10000),
1507 ]
1508 }
1509 );
1510 }
1511
1512 #[test]
1513 fn arrays() {
1514 let m = dec(
1515 "8E920950003392020000200002000000000000000000100000000000000020000000000000003000000000000000400",
1516 );
1517 let item = |s: &str| ArrayItem::Item(Box::new(Object::Real { value: real(s) }));
1518 assert_eq!(
1519 m,
1520 Object::Array {
1521 dims: vec![2, 2],
1522 items: vec![
1523 ArrayItem::Row(vec![item("1"), item("2")]),
1524 ArrayItem::Row(vec![item("3"), item("4")])
1525 ],
1526 }
1527 );
1528 assert_eq!(
1529 serde_json::to_value(&m).unwrap(),
1530 json!({"type": "array", "dims": [2, 2], "items": [
1531 [{"type": "real", "value": 1.0}, {"type": "real", "value": 2.0}],
1532 [{"type": "real", "value": 3.0}, {"type": "real", "value": 4.0}]]})
1533 );
1534 let v = dec(
1535 "8E9204500077920100002000000000000000000100000000000000020000000000000003000000000000000400",
1536 );
1537 let Object::Array { dims, items } = v else {
1538 panic!()
1539 };
1540 assert_eq!(dims, vec![2]);
1541 assert_eq!(
1542 items[1],
1543 ArrayItem::Item(Box::new(Object::Complex {
1544 re: real("3"),
1545 im: real("4")
1546 }))
1547 );
1548 }
1549
1550 #[test]
1551 fn array_dimensions_cannot_overflow() {
1552 let mut o = nib("8E920");
1555 put_field(&mut o, 0x23, 5);
1556 put_field(&mut o, 0x02933, 5);
1557 put_field(&mut o, 4, 5);
1558 for d in [0x80000u64, 0x80000, 0x80000, 8] {
1559 put_field(&mut o, d, 5);
1560 }
1561 assert_eq!(o.len(), 40);
1562 let Ok(Object::Unknown { prolog, .. }) = decode(&o, &NoMemory) else {
1563 panic!("decoded");
1564 };
1565 assert_eq!(prolog, "029E8");
1566 let mut e = nib("8E920");
1568 put_field(&mut e, 25, 5);
1569 put_field(&mut e, 0x02933, 5);
1570 put_field(&mut e, 2, 5);
1571 put_field(&mut e, 3, 5);
1572 put_field(&mut e, 0, 5);
1573 assert_eq!(
1574 decode(&e, &NoMemory).unwrap(),
1575 Object::Array {
1576 dims: vec![3, 0],
1577 items: vec![
1578 ArrayItem::Row(vec![]),
1579 ArrayItem::Row(vec![]),
1580 ArrayItem::Row(vec![])
1581 ],
1582 }
1583 );
1584 let mut z = nib("8E920");
1585 put_field(&mut z, 30, 5);
1586 put_field(&mut z, 0x02933, 5);
1587 put_field(&mut z, 3, 5);
1588 for d in [0x80000u64, 0x80000, 0] {
1589 put_field(&mut z, d, 5);
1590 }
1591 assert!(matches!(
1592 decode(&z, &NoMemory).unwrap(),
1593 Object::Unknown { .. }
1594 ));
1595 }
1596
1597 fn dims_array(ndims: usize) -> Vec<u8> {
1599 let mut o = nib("8E920");
1600 put_field(&mut o, (15 + 5 * ndims + 16) as u64, 5);
1601 put_field(&mut o, 0x02933, 5);
1602 put_field(&mut o, ndims as u64, 5);
1603 for _ in 0..ndims {
1604 put_field(&mut o, 1, 5);
1605 }
1606 o.extend(nib("0000000000000010"));
1607 o
1608 }
1609
1610 #[test]
1611 fn arrays_have_at_most_two_dimensions() {
1612 let item = ArrayItem::Item(Box::new(Object::Real { value: real("1") }));
1613 assert_eq!(
1614 dec(&hex(&dims_array(2))),
1615 Object::Array {
1616 dims: vec![1, 1],
1617 items: vec![ArrayItem::Row(vec![item])],
1618 }
1619 );
1620 for ndims in [3, 12_000] {
1623 let Ok(Object::Unknown { prolog, .. }) = decode(&dims_array(ndims), &NoMemory) else {
1624 panic!("{ndims} dimensions decoded");
1625 };
1626 assert_eq!(prolog, "029E8");
1627 let mut list = nib("47A20");
1628 list.extend(dims_array(ndims));
1629 list.extend(nib("B2130"));
1630 let e = Reader::new(&NoMemory)
1631 .decoder
1632 .object(&list, 0, 0)
1633 .unwrap_err();
1634 assert!(e.to_string().contains("dimensions"), "{e:#}");
1635 }
1636 }
1637}