/ cab / runtime / code.rs
code.rs
  1  use std::{
  2     cell::RefCell,
  3     collections::VecDeque,
  4     fmt::{
  5        self,
  6        Write as _,
  7     },
  8     ops::{
  9        self,
 10        Add as _,
 11     },
 12  };
 13  
 14  use derive_more::{
 15     Deref,
 16     DerefMut,
 17  };
 18  use dup::Dupe as _;
 19  use ranged::Span;
 20  use ust::{
 21     COLORS,
 22     Display,
 23     INDENT_WIDTH,
 24     STYLE_GUTTER,
 25     Write,
 26     style::{
 27        self,
 28        StyledExt as _,
 29     },
 30     terminal::{
 31        self,
 32        DOT,
 33        LEFT_TO_RIGHT,
 34        RIGHT_TO_BOTTOM,
 35        TOP_TO_BOTTOM,
 36     },
 37     with,
 38     write,
 39  };
 40  
 41  use crate::{
 42     Argument,
 43     Operation,
 44     Value,
 45     value,
 46  };
 47  
 48  const ENCODED_U64_LEN_MAX: usize = 9;
 49  const ENCODED_U16_LEN_MAX: usize = 0_u16.to_le_bytes().len();
 50  const ENCODED_OPERATION_LEN: usize = 1;
 51  
 52  #[derive(Deref, DerefMut, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
 53  pub struct ByteIndex(usize);
 54  
 55  impl ByteIndex {
 56     #[must_use]
 57     pub fn dummy() -> Self {
 58        Self(usize::MAX)
 59     }
 60  }
 61  
 62  #[derive(Deref, DerefMut, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
 63  pub struct ValueIndex(usize);
 64  
 65  impl ValueIndex {
 66     #[must_use]
 67     pub fn dummy() -> Self {
 68        Self(usize::MAX)
 69     }
 70  }
 71  
 72  pub struct Code {
 73     bytes: Vec<u8>,
 74  
 75     path:  value::Path,
 76     spans: Vec<(ByteIndex, Span)>,
 77  
 78     values: Vec<Value>,
 79  }
 80  
 81  impl Display for Code {
 82     fn display_styled(&self, writer: &mut dyn Write) -> fmt::Result {
 83        const STYLE_JUMP_ADDRESS: style::Style = style::Color::BrightYellow.fg().bold().underline();
 84  
 85        enum CodeType {
 86           Suspend,
 87           Lambda,
 88        }
 89  
 90        let mut codes = VecDeque::from([(0_usize, CodeType::Suspend, self)]);
 91  
 92        while let Some((code_index, code_type, code)) = codes.pop_back() {
 93           let highlighted = RefCell::new(Vec::<ByteIndex>::new());
 94  
 95           // INDENT: "0x123 | "
 96           let index_width = 2 + terminal::number_hex_width(code.bytes.len() - 1);
 97           let indent_index = RefCell::new(ByteIndex(0));
 98           let mut index_previous = None::<usize>;
 99           terminal::indent!(writer, index_width + 3, |writer| {
100              let index = *indent_index.borrow();
101  
102              let style = if highlighted.borrow().contains(&index) {
103                 STYLE_JUMP_ADDRESS
104              } else {
105                 STYLE_GUTTER
106              };
107  
108              let index = *index;
109  
110              with(writer, style, |writer| {
111                 if index_previous == Some(index) {
112                    let dot_width = 2 + terminal::number_hex_width(index);
113                    let space_width = index_width - dot_width;
114  
115                    write!(writer, "{:>space_width$}", "")?;
116  
117                    for _ in 0..dot_width {
118                       write!(writer, "{DOT}")?;
119                    }
120                 } else {
121                    write!(writer, "{index:>#index_width$X}")?;
122                 }
123  
124                 write!(writer, " {TOP_TO_BOTTOM}")
125              })?;
126  
127              index_previous.replace(index);
128              Ok(index_width + 2)
129           });
130  
131           if **indent_index.borrow() < code.bytes.len() {
132              // DEDENT: "| "
133              terminal::dedent!(writer, 2);
134  
135              // INDENT: "┏━━━ ".
136              terminal::indent!(
137                 writer,
138                 header =
139                    const_str::concat!(RIGHT_TO_BOTTOM, LEFT_TO_RIGHT, LEFT_TO_RIGHT, LEFT_TO_RIGHT)
140                       .style(STYLE_GUTTER)
141              );
142  
143              with(writer, style::Color::Red.fg().bold(), |writer| {
144                 write!(writer, "{code_index:#X} ")
145              })?;
146  
147              match code_type {
148                 CodeType::Suspend => write(writer, &"(suspend)".cyan().bold())?,
149                 CodeType::Lambda => write(writer, &"(lambda)".magenta().bold())?,
150              }
151           }
152  
153           let mut indent: usize = 0;
154  
155           let mut items = code.iter().peekable();
156           while let Some((index, item)) = items.next() {
157              *indent_index.borrow_mut() = index;
158  
159              match item {
160                 CodeItem::Operation(operation) => {
161                    terminal::indent!(
162                       writer,
163                       (indent - usize::from(operation == Operation::ScopeEnd))
164                          * INDENT_WIDTH as usize,
165                    );
166  
167                    writeln!(writer)?;
168  
169                    if operation == Operation::ScopeEnd {
170                       indent -= 1;
171                       write(writer, &"}".style(COLORS[indent % COLORS.len()]))?;
172                       write!(writer, " ")?;
173                    }
174  
175                    with(writer, style::Color::Yellow.fg(), |writer| {
176                       write!(writer, "{operation:?}")
177                    })?;
178  
179                    if operation == Operation::ScopeStart {
180                       write!(writer, " ")?;
181                       write(writer, &"{".style(COLORS[indent % COLORS.len()]))?;
182                       indent += 1;
183                    }
184  
185                    if let Some(&(_, CodeItem::Argument(_))) = items.peek() {
186                       write(writer, &'('.bright_black().bold())?;
187                    }
188                 },
189  
190                 CodeItem::Argument(argument) => {
191                    match argument {
192                       Argument::U16(u16) => write(writer, &u16.magenta())?,
193  
194                       Argument::U64(u64) => write(writer, &u64.blue())?,
195  
196                       Argument::ValueIndex(value_index) => {
197                          let value_index_unique = code_index.add(2) * value_index.add(2);
198  
199                          with(writer, style::Color::Blue.fg().bold(), |writer| {
200                             write!(writer, "{value_index:#X} ", value_index = *value_index)
201                          })?;
202  
203                          match code[value_index] {
204                             Value::Code {
205                                is_lambda,
206                                ref code,
207                             } => {
208                                codes.push_front((
209                                   value_index_unique,
210                                   if is_lambda {
211                                      CodeType::Lambda
212                                   } else {
213                                      CodeType::Suspend
214                                   },
215                                   code,
216                                ));
217  
218                                write(writer, &"-> ".bright_black().bold())?;
219                                with(writer, style::Color::Red.fg().bold(), |writer| {
220                                   write!(writer, "{value_index_unique:#X}")
221                                })?;
222                             },
223  
224                             ref value => {
225                                // TODO: This isn't indented as our indenter:
226                                //
227                                // 1. Doesn't support dynamic indent widths. This is easily fixed by
228                                //    storing an indent (like std::iter::continuations) and measuring
229                                //    the length in IndentWrite::width.
230                                //
231                                // 2. Doesn't support specifying `Place`.
232                                //
233                                // Fix this soon, maybe --RGB.
234                                write(writer, &":: ".bright_black().bold())?;
235                                value.display_styled(writer)?;
236                             },
237                          }
238                       },
239  
240                       Argument::ByteIndex(byte_index) => {
241                          highlighted.borrow_mut().push(byte_index);
242  
243                          with(writer, STYLE_JUMP_ADDRESS, |writer| {
244                             write!(writer, "{byte_index:#X}", byte_index = *byte_index)
245                          })?;
246                       },
247                    }
248  
249                    let delimiter = match items.peek() {
250                       Some(&(_, CodeItem::Argument(_))) => ", ",
251                       _ => ")",
252                    };
253  
254                    write(writer, &delimiter.bright_black().bold())?;
255                 },
256              }
257           }
258  
259           if !codes.is_empty() {
260              writeln!(writer)?;
261              writeln!(writer)?;
262           }
263        }
264  
265        Ok(())
266     }
267  }
268  
269  #[derive(Debug, Clone, Copy, PartialEq, Eq)]
270  pub enum CodeItem {
271     Operation(Operation),
272     Argument(Argument),
273  }
274  
275  impl CodeItem {
276     pub fn as_operation(&self) -> Option<&Operation> {
277        if let &Self::Operation(ref operation) = self {
278           Some(operation)
279        } else {
280           None
281        }
282     }
283  
284     pub fn as_argument(&self) -> Option<&Argument> {
285        if let &Self::Argument(ref argument) = self {
286           Some(argument)
287        } else {
288           None
289        }
290     }
291  }
292  
293  impl Code {
294     pub fn value(&mut self, value: Value) -> ValueIndex {
295        let index = ValueIndex(self.values.len());
296        self.values.push(value);
297        index
298     }
299  
300     pub fn iter(&self) -> impl Iterator<Item = (ByteIndex, CodeItem)> {
301        gen move {
302           let mut index = ByteIndex(0);
303  
304           while *index < self.bytes.len() {
305              let (_, operation, size) = self.read_operation(index);
306  
307              yield (index, CodeItem::Operation(operation));
308              *index += size;
309  
310              match operation {
311                 Operation::Push => {
312                    let (value, size) = self.read_u64(index);
313  
314                    yield (
315                       index,
316                       CodeItem::Argument(Argument::ValueIndex(ValueIndex(
317                          usize::try_from(value).expect("value index must be valid"),
318                       ))),
319                    );
320  
321                    *index += size;
322                 },
323  
324                 Operation::Jump | Operation::JumpIf | Operation::JumpIfError => {
325                    let (value, size) = self.read_u16(index);
326  
327                    yield (
328                       index,
329                       CodeItem::Argument(Argument::ByteIndex(ByteIndex(usize::from(value)))),
330                    );
331  
332                    *index += size;
333                 },
334  
335                 Operation::Interpolate => {
336                    let (value, size) = self.read_u64(index);
337  
338                    yield (index, CodeItem::Argument(Argument::U64(value)));
339  
340                    *index += size;
341                 },
342  
343                 _ => {},
344              }
345           }
346        }
347     }
348  }
349  
350  impl ops::Index<ValueIndex> for Code {
351     type Output = Value;
352  
353     fn index(&self, index: ValueIndex) -> &Self::Output {
354        self.values.get(*index).expect("value index must be valid")
355     }
356  }
357  
358  impl Code {
359     #[must_use]
360     pub fn new(path: value::Path) -> Self {
361        Self {
362           bytes: Vec::new(),
363  
364           path,
365           spans: Vec::new(),
366  
367           values: Vec::new(),
368        }
369     }
370  
371     #[must_use]
372     pub fn path(&self) -> &value::Path {
373        &self.path
374     }
375  
376     pub fn push_u64(&mut self, data: u64) -> ByteIndex {
377        let mut encoded = [0; ENCODED_U64_LEN_MAX];
378        let len = vu128::encode_u64(&mut encoded, data);
379  
380        let index = ByteIndex(self.bytes.len());
381        self.bytes.extend_from_slice(&encoded[..len]);
382        index
383     }
384  
385     #[must_use]
386     pub fn read_u64(&self, index: ByteIndex) -> (u64, usize) {
387        let encoded = match self.bytes.get(*index..*index + ENCODED_U64_LEN_MAX) {
388           Some(slice) => {
389              <[u8; ENCODED_U64_LEN_MAX]>::try_from(slice).expect("size was statically checked")
390           },
391  
392           None => {
393              let mut buffer = [0; ENCODED_U64_LEN_MAX];
394              buffer[..self.bytes.len() - *index]
395                 .copy_from_slice(self.bytes.get(*index..).expect("byte index must be valid"));
396              buffer
397           },
398        };
399  
400        vu128::decode_u64(&encoded)
401     }
402  
403     pub fn push_u16(&mut self, data: u16) -> ByteIndex {
404        let index = ByteIndex(self.bytes.len());
405        self.bytes.extend_from_slice(&data.to_le_bytes());
406        index
407     }
408  
409     #[must_use]
410     pub fn read_u16(&self, index: ByteIndex) -> (u16, usize) {
411        let encoded = <[u8; ENCODED_U16_LEN_MAX]>::try_from(
412           self
413              .bytes
414              .get(*index..*index + ENCODED_U16_LEN_MAX)
415              .expect("byte index must be valid"),
416        )
417        .expect("size was statically checked");
418  
419        (u16::from_le_bytes(encoded), ENCODED_U16_LEN_MAX)
420     }
421  
422     pub fn push_operation(&mut self, span: Span, operation: Operation) -> ByteIndex {
423        let index = ByteIndex(self.bytes.len());
424        self.bytes.push(operation as _);
425  
426        // No need to insert the span again if this instruction was created from the
427        // last span.
428        if self
429           .spans
430           .last()
431           .is_none_or(|&(_, last_span)| last_span != span)
432        {
433           self.spans.push((index, span));
434        }
435  
436        index
437     }
438  
439     #[must_use]
440     #[track_caller]
441     pub fn read_operation(&self, index: ByteIndex) -> (value::Location, Operation, usize) {
442        let position = self.spans.partition_point(|&(index2, _)| index >= index2);
443  
444        let (_, span) = self.spans[position.saturating_sub(1)];
445  
446        (
447           value::Location::new(self.path.dupe(), span),
448           Operation::try_from(self.bytes[*index]).expect("byte index must be valid"),
449           ENCODED_OPERATION_LEN,
450        )
451     }
452  
453     pub fn point_here(&mut self, index: ByteIndex) {
454        let here = u16::try_from(self.bytes.len()).expect("bytes len must fit in u16");
455  
456        self.bytes[*index..*index + ENCODED_U16_LEN_MAX].copy_from_slice(&here.to_le_bytes());
457     }
458  }